shim_spec.js 11.5 KB
Newer Older
1
define([
2
    'jquery', 'underscore', 'annotator_1.2.9', 'js/edxnotes/views/notes_factory'
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
], function($, _, Annotator, NotesFactory) {
    'use strict';
    describe('EdxNotes Shim', function() {
        var annotators, highlights;

        function checkAnnotatorIsFrozen(annotator) {
            expect(annotator.isFrozen).toBe(true);
            expect(annotator.onHighlightMouseover).not.toHaveBeenCalled();
            expect(annotator.startViewerHideTimer).not.toHaveBeenCalled();
        }

        function checkAnnotatorIsUnfrozen(annotator) {
            expect(annotator.isFrozen).toBe(false);
            expect(annotator.onHighlightMouseover).toHaveBeenCalled();
            expect(annotator.startViewerHideTimer).toHaveBeenCalled();
        }

        function checkClickEventsNotBound(namespace) {
            var events = $._data(document, 'events').click;

            _.each(events, function(event) {
                expect(event.namespace.indexOf(namespace)).toBe(-1);
            });
        }

        beforeEach(function() {
            loadFixtures('js/fixtures/edxnotes/edxnotes_wrapper.html');
            highlights = [];
            annotators = [
polesye committed
32
                NotesFactory.factory($('#edx-notes-wrapper-123').get(0), {
33 34
                    endpoint: 'http://example.com/'
                }),
polesye committed
35
                NotesFactory.factory($('#edx-notes-wrapper-456').get(0), {
36 37 38
                    endpoint: 'http://example.com/'
                })
            ];
polesye committed
39 40
            _.each(annotators, function(annotator) {
                highlights.push($('<span class="annotator-hl" />').appendTo(annotator.element));
41 42 43
                spyOn(annotator, 'onHighlightClick').and.callThrough();
                spyOn(annotator, 'onHighlightMouseover').and.callThrough();
                spyOn(annotator, 'startViewerHideTimer').and.callThrough();
44
            });
45
            spyOn($.fn, 'off').and.callThrough();
46 47
        });

48
        afterEach(function() {
49 50 51
            while (Annotator._instances.length > 0) {
                Annotator._instances[0].destroy();
            }
52 53
        });

54 55 56 57 58
        it('does not show the viewer if the editor is opened', function() {
            annotators[0].showEditor({}, {});
            highlights[0].mouseover();
            expect($('#edx-notes-wrapper-123 .annotator-editor')).not.toHaveClass('annotator-hide');
            expect($('#edx-notes-wrapper-123 .annotator-viewer')).toHaveClass('annotator-hide');
polesye committed
59
        });
polesye committed
60 61

        it('clicking on highlights does not open the viewer when the editor is opened', function() {
62
            spyOn(annotators[1].editor, 'isShown').and.returnValue(false);
polesye committed
63
            highlights[0].click();
64
            annotators[1].editor.isShown.and.returnValue(true);
polesye committed
65 66 67
            highlights[1].click();
            expect($('#edx-notes-wrapper-123 .annotator-viewer')).not.toHaveClass('annotator-hide');
            expect($('#edx-notes-wrapper-456 .annotator-viewer')).toHaveClass('annotator-hide');
68 69
        });

70 71 72 73 74 75 76 77 78
        it('clicking a highlight freezes mouseover and mouseout in all highlighted text', function() {
            _.each(annotators, function(annotator) {
                expect(annotator.isFrozen).toBe(false);
            });
            highlights[0].click();
            // Click is attached to the onHighlightClick event handler which
            // in turn calls onHighlightMouseover.
            // To test if onHighlightMouseover is called or not on
            // mouseover, we'll have to reset onHighlightMouseover.
79
            annotators[0].onHighlightMouseover.calls.reset();
80 81 82 83 84 85 86 87 88
            // Check that both instances of annotator are frozen
            _.invoke(highlights, 'mouseover');
            _.invoke(highlights, 'mouseout');
            _.each(annotators, checkAnnotatorIsFrozen);
        });

        it('clicking twice reverts to default behavior', function() {
            highlights[0].click();
            $(document).click();
89
            annotators[0].onHighlightMouseover.calls.reset();
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118

            // Check that both instances of annotator are unfrozen
            _.invoke(highlights, 'mouseover');
            _.invoke(highlights, 'mouseout');
            _.each(annotators, function(annotator) {
                checkAnnotatorIsUnfrozen(annotator);
            });
        });

        it('destroying an instance with an open viewer sets all other instances' +
           'to unfrozen and unbinds document click.edxnotes:freeze event handlers', function() {
            // Freeze all instances
            highlights[0].click();
            // Destroy first instance
            annotators[0].destroy();

            // Check that all click.edxnotes:freeze are unbound
            checkClickEventsNotBound('edxnotes:freeze');

            // Check that the remaining instance is unfrozen
            highlights[1].mouseover();
            highlights[1].mouseout();
            checkAnnotatorIsUnfrozen(annotators[1]);
        });

        it('destroying an instance with an closed viewer only unfreezes that instance' +
           'and unbinds one document click.edxnotes:freeze event handlers', function() {
            // Freeze all instances
            highlights[0].click();
119
            annotators[0].onHighlightMouseover.calls.reset();
120 121 122 123 124 125 126 127 128 129 130 131
            // Destroy second instance
            annotators[1].destroy();

            // Check that the first instance is frozen
            highlights[0].mouseover();
            highlights[0].mouseout();
            checkAnnotatorIsFrozen(annotators[0]);

            // Check that second one doesn't have a bound click.edxnotes:freeze
            checkClickEventsNotBound('edxnotes:freeze' + annotators[1].uid);
        });

polesye committed
132
        it('should unbind events on destruction', function() {
133 134
            annotators[0].destroy();
            expect($.fn.off).toHaveBeenCalledWith(
polesye committed
135 136 137 138
                'click', annotators[0].onNoteClick
            );
            expect($.fn.off).toHaveBeenCalledWith(
                'click', '.annotator-hl'
139 140
            );
        });
141

142 143 144 145
        it('should hide viewer when close button is clicked', function() {
            var close,
                annotation = {
                    id: '01',
146
                    text: 'Test text',
147 148 149 150 151 152 153 154 155
                    highlights: [highlights[0].get(0)]
                };

            annotators[0].viewer.load([annotation]);
            close = annotators[0].viewer.element.find('.annotator-close');
            close.click();
            expect($('#edx-notes-wrapper-123 .annotator-viewer')).toHaveClass('annotator-hide');
        });

156
        describe('_setupViewer', function() {
157 158
            var mockViewer = null;

159
            beforeEach(function() {
Eric Fischer committed
160
                var $element = $('<div />');
161 162
                mockViewer = {
                    fields: [],
Eric Fischer committed
163
                    element: $element
164 165
                };

166 167 168
                mockViewer.on = jasmine.createSpy().and.returnValue(mockViewer);
                mockViewer.hide = jasmine.createSpy().and.returnValue(mockViewer);
                mockViewer.destroy = jasmine.createSpy().and.returnValue(mockViewer);
169
                mockViewer.addField = jasmine.createSpy().and.callFake(function(options) {
170 171 172 173
                    mockViewer.fields.push(options);
                    return mockViewer;
                });

Eric Fischer committed
174 175
                spyOn($element, 'bind').and.returnValue($element);
                spyOn($element, 'appendTo').and.returnValue($element);
176
                spyOn(Annotator, 'Viewer').and.returnValue(mockViewer);
177 178 179 180

                annotators[0]._setupViewer();
            });

181
            it('should create a new instance of Annotator.Viewer and set Annotator#viewer', function() {
182 183 184
                expect(annotators[0].viewer).toEqual(mockViewer);
            });

185
            it('should hide the annotator on creation', function() {
186
                expect(mockViewer.hide.calls.count()).toBe(1);
187 188
            });

189
            it('should setup the default text field', function() {
190
                var args = mockViewer.addField.calls.mostRecent().args[0];
191

192
                expect(mockViewer.addField.calls.count()).toBe(1);
193 194 195
                expect(_.isFunction(args.load)).toBeTruthy();
            });

196
            it('should set the contents of the field on load', function() {
197 198 199 200 201 202 203
                var field = document.createElement('div'),
                    annotation = {text: 'text \nwith\r\nline\n\rbreaks \r'};

                annotators[0].viewer.fields[0].load(field, annotation);
                expect($(field).html()).toBe('text <br>with<br>line<br>breaks <br>');
            });

204
            it('should set the contents of the field to placeholder text when empty', function() {
205 206 207 208 209 210 211
                var field = document.createElement('div'),
                    annotation = {text: ''};

                annotators[0].viewer.fields[0].load(field, annotation);
                expect($(field).html()).toBe('<i>No Comment</i>');
            });

212
            it('should setup the default text field to publish an event on load', function() {
213 214 215 216 217 218 219 220 221
                var field = document.createElement('div'),
                    annotation = {text: ''},
                    callback = jasmine.createSpy();

                annotators[0].on('annotationViewerTextField', callback);
                annotators[0].viewer.fields[0].load(field, annotation);
                expect(callback).toHaveBeenCalledWith(field, annotation);
            });

222
            it('should subscribe to custom events', function() {
223 224 225 226
                expect(mockViewer.on).toHaveBeenCalledWith('edit', annotators[0].onEditAnnotation);
                expect(mockViewer.on).toHaveBeenCalledWith('delete', annotators[0].onDeleteAnnotation);
            });

227
            it('should bind to browser mouseover and mouseout events', function() {
228
                expect(mockViewer.element.bind).toHaveBeenCalledWith({
Eric Fischer committed
229 230
                    mouseover: annotators[0].clearViewerHideTimer,
                    mouseout: annotators[0].startViewerHideTimer
231 232 233
                });
            });

234
            it('should append the Viewer#element to the Annotator#wrapper', function() {
235 236 237
                expect(mockViewer.element.appendTo).toHaveBeenCalledWith(annotators[0].wrapper);
            });
        });
cahrens committed
238

239
        describe('TagsPlugin', function() {
cahrens committed
240 241 242 243
            it('should add ARIA label information to the viewer', function() {
                var tagDiv,
                    annotation = {
                        id: '01',
244 245
                        text: 'Test text',
                        tags: ['tag1', 'tag2', 'tag3'],
cahrens committed
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
                        highlights: [highlights[0].get(0)]
                    };

                annotators[0].viewer.load([annotation]);
                tagDiv = annotators[0].viewer.element.find('.annotator-tags');
                expect($(tagDiv).attr('role')).toEqual('region');
                expect($(tagDiv).attr('aria-label')).toEqual('tags');

                // Three children for the individual tags.
                expect($(tagDiv).children().length).toEqual(3);
            });

            it('should add screen reader label to the editor', function() {
                var srLabel, editor, inputId;

                // We don't know exactly what the input ID will be (depends on number of annotatable components shown),
                // but the sr label "for" attribute should match the ID of the element immediately following it.
                annotators[0].showEditor({}, {});
                editor = annotators[0].editor;
265
                srLabel = editor.element.find('label.sr');
cahrens committed
266 267 268 269
                inputId = srLabel.next().attr('id');
                expect(srLabel.attr('for')).toEqual(inputId);
            });
        });
270 271
    });
});