Files
edx-platform/lms/static/js/spec/edxnotes/plugins/accessibility_spec.js
Syed Ali Abbas Zaidi 8480dbc228 chore: apply amnesty on existing not fixable issues (#32215)
* fix: eslint operator-linebreak issue

* fix: eslint quotes issue

* fix: react jsx indent and props issues

* fix: eslint trailing spaces issues

* fix: eslint line around directives issue

* fix: eslint semi rule

* fix: eslint newline per chain rule

* fix: eslint space infix ops rule

* fix: eslint space-in-parens issue

* fix: eslint space before function paren issue

* fix: eslint space before blocks issue

* fix: eslint arrow body style issue

* fix: eslint dot-location issue

* fix: eslint quotes issue

* fix: eslint quote props issue

* fix: eslint operator assignment issue

* fix: eslint new line after import issue

* fix: indent issues

* fix: operator assignment issue

* fix: all autofixable eslint issues

* fix: all react related fixable issues

* fix: autofixable eslint issues

* chore: remove all template literals

* fix: remaining autofixable issues

* chore: apply amnesty on all existing issues

* fix: failing xss-lint issues

* refactor: apply amnesty on remaining issues

* refactor: apply amnesty on new issues

* fix: remove file level suppressions

* refactor: apply amnesty on new issues
2023-08-07 19:13:19 +05:00

329 lines
14 KiB
JavaScript

define([
'jquery', 'underscore', 'annotator_1.2.9', 'logger', 'js/edxnotes/views/notes_factory'
], function($, _, Annotator, Logger, NotesFactory) {
'use strict';
describe('EdxNotes Accessibility Plugin', function() {
function keyDownEvent(key) {
return $.Event('keydown', {keyCode: key});
}
function tabBackwardEvent() {
return $.Event('keydown', {keyCode: $.ui.keyCode.TAB, shiftKey: true});
}
function tabForwardEvent() {
return $.Event('keydown', {keyCode: $.ui.keyCode.TAB, shiftKey: false});
}
function enterMetaKeyEvent() {
return $.Event('keydown', {keyCode: $.ui.keyCode.ENTER, metaKey: true});
}
function enterControlKeyEvent() {
return $.Event('keydown', {keyCode: $.ui.keyCode.ENTER, ctrlKey: true});
}
beforeEach(function() {
this.KEY = $.ui.keyCode;
loadFixtures('js/fixtures/edxnotes/edxnotes_wrapper.html');
this.annotator = NotesFactory.factory(
$('div#edx-notes-wrapper-123').get(0), {
endpoint: 'http://example.com/'
}
);
this.plugin = this.annotator.plugins.Accessibility;
spyOn(Logger, 'log');
});
afterEach(function() {
while (Annotator._instances.length > 0) {
Annotator._instances[0].destroy();
}
});
describe('destroy', function() {
it('should unbind all events', function() {
spyOn($.fn, 'off');
spyOn(this.annotator, 'unsubscribe').and.callThrough();
this.plugin.destroy();
expect(this.annotator.unsubscribe).toHaveBeenCalledWith(
'annotationViewerTextField', this.plugin.addAriaAttributes
);
expect(this.annotator.unsubscribe).toHaveBeenCalledWith(
'annotationsLoaded', this.plugin.addDescriptions
);
expect(this.annotator.unsubscribe).toHaveBeenCalledWith(
'annotationCreated', this.plugin.addDescriptions
);
expect(this.annotator.unsubscribe).toHaveBeenCalledWith(
'annotationCreated', this.plugin.focusOnHighlightedText
);
expect(this.annotator.unsubscribe).toHaveBeenCalledWith(
'annotationDeleted', this.plugin.removeDescription
);
expect($.fn.off).toHaveBeenCalledWith('.accessibility');
});
});
describe('a11y attributes', function() {
var highlight, annotation, $note;
beforeEach(function() {
highlight = $('<span class="annotator-hl" tabindex="0"/>').appendTo(this.annotator.element);
annotation = {
id: '01',
text: 'Test text',
highlights: [highlight.get(0)]
};
});
it('should be added to highlighted text and associated note', function() {
this.annotator.viewer.load([annotation]);
$note = $('.annotator-note');
expect($note).toExist();
expect($note).toHaveAttr('tabindex', '-1');
expect($note).toHaveAttr('role', 'note');
expect($note).toHaveAttr('class', 'annotator-note');
});
it('should create aria-descriptions when annotations are loaded', function() {
this.annotator.publish('annotationsLoaded', [[annotation]]);
expect(highlight).toHaveAttr('aria-describedby', 'aria-note-description-01');
expect($('#aria-note-description-01')).toContainText('Test text');
});
it('should create aria-description when new annotation is created', function() {
this.annotator.publish('annotationCreated', [annotation]);
expect(highlight).toHaveAttr('aria-describedby', 'aria-note-description-01');
expect($('#aria-note-description-01')).toContainText('Test text');
});
it('should remove aria-description when the annotation is removed', function() {
this.annotator.publish('annotationDeleted', [annotation]);
expect($('#aria-note-description-01')).not.toExist();
});
});
describe('keydown events on highlighted text', function() {
var highlight, annotation, note;
beforeEach(function() {
highlight = $('<span class="annotator-hl" tabindex="0"/>').appendTo(this.annotator.element);
annotation = {
id: '01',
text: 'Test text',
highlights: [highlight.get(0)]
};
highlight.data('annotation', annotation);
spyOn(this.annotator, 'showViewer').and.callThrough();
spyOn(this.annotator.viewer, 'hide').and.callThrough();
spyOn(this.plugin, 'focusOnGrabber').and.callThrough();
});
it('should open the viewer on SPACE keydown and focus on note', function() {
highlight.trigger(keyDownEvent(this.KEY.SPACE));
expect(this.annotator.showViewer).toHaveBeenCalled();
});
it('should open the viewer on ENTER keydown and focus on note', function() {
highlight.trigger(keyDownEvent(this.KEY.ENTER));
expect(this.annotator.showViewer).toHaveBeenCalled();
});
// This happens only when coming from notes page
it('should open focus on viewer on TAB keydown if viewer is opened', function() {
this.annotator.viewer.load([annotation]);
highlight.trigger(keyDownEvent(this.KEY.TAB));
expect(this.annotator.element.find('.annotator-listing')).toBeFocused();
});
it('should focus highlighted text after closing', function() {
// eslint-disable-next-line no-shadow
var note;
highlight.trigger(keyDownEvent(this.KEY.ENTER));
note = this.annotator.element.find('.annotator-edit');
note.trigger(keyDownEvent(this.KEY.ESCAPE));
expect(highlight).toBeFocused();
});
it('should focus on grabber after being deleted', function() {
highlight.trigger(keyDownEvent(this.KEY.ENTER));
this.annotator.publish('annotationDeleted', {});
expect(this.plugin.focusGrabber).toBeFocused();
});
it('should not focus on grabber when the viewer is hidden', function() {
this.annotator.publish('annotationDeleted', {});
expect(this.plugin.focusGrabber).not.toBeFocused();
});
});
describe('keydown events on viewer', function() {
var highlight, annotation, listing, note, edit, del, close;
beforeEach(function() {
highlight = $('<span class="annotator-hl" tabindex="0"/>').appendTo(this.annotator.element);
annotation = {
id: '01',
text: 'Test text',
highlights: [highlight.get(0)]
};
highlight.data('annotation', annotation);
this.annotator.viewer.load([annotation]);
listing = this.annotator.element.find('.annotator-listing').first();
note = this.annotator.element.find('.annotator-note').first();
edit = this.annotator.element.find('.annotator-edit').first();
del = this.annotator.element.find('.annotator-delete').first();
close = this.annotator.element.find('.annotator-close').first();
spyOn(this.annotator.viewer, 'hide').and.callThrough();
});
it('should give focus to Note on Listing TAB keydown', function() {
listing.focus();
listing.trigger(tabForwardEvent());
expect(note).toBeFocused();
});
it('should give focus to Close on Listing SHIFT + TAB keydown', function() {
listing.focus();
listing.trigger(tabBackwardEvent());
expect(close).toBeFocused();
});
it('should cycle forward through Note, Edit, Delete, and Close on TAB keydown', function() {
note.focus();
note.trigger(tabForwardEvent());
expect(edit).toBeFocused();
edit.trigger(tabForwardEvent());
expect(del).toBeFocused();
del.trigger(tabForwardEvent());
expect(close).toBeFocused();
close.trigger(tabForwardEvent());
expect(note).toBeFocused();
});
it('should cycle backward through Note, Edit, Delete, and Close on SHIFT + TAB keydown', function() {
note.focus();
note.trigger(tabBackwardEvent());
expect(close).toBeFocused();
close.trigger(tabBackwardEvent());
expect(del).toBeFocused();
del.trigger(tabBackwardEvent());
expect(edit).toBeFocused();
edit.trigger(tabBackwardEvent());
expect(note).toBeFocused();
});
it('should hide on ESCAPE keydown', function() {
var tabControls = [listing, note, edit, del, close];
_.each(tabControls, function(control) {
control.focus();
control.trigger(keyDownEvent(this.KEY.ESCAPE));
}, this);
expect(this.annotator.viewer.hide.calls.count()).toBe(5);
});
});
describe('keydown events on editor', function() {
var highlight, annotation, form, annotatorItems, textArea, tags, save, cancel;
beforeEach(function() {
highlight = $('<span class="annotator-hl" tabindex="0"/>').appendTo(this.annotator.element);
annotation = {
id: '01',
text: 'Test text',
highlights: [highlight.get(0)]
};
highlight.data('annotation', annotation);
this.annotator.editor.show(annotation, {left: 0, top: 0});
form = this.annotator.element.find('form.annotator-widget');
annotatorItems = this.annotator.element.find('.annotator-item');
textArea = annotatorItems.first().children('textarea');
tags = annotatorItems.first().next().children('input');
save = this.annotator.element.find('.annotator-save');
cancel = this.annotator.element.find('.annotator-cancel');
spyOn(this.annotator.editor, 'submit').and.callThrough();
spyOn(this.annotator.editor, 'hide').and.callThrough();
});
it('should give focus to TextArea on Form TAB keydown', function() {
form.focus();
form.trigger(tabForwardEvent());
expect(textArea).toBeFocused();
});
it('should give focus to Cancel on Form SHIFT + TAB keydown', function() {
form.focus();
form.trigger(tabBackwardEvent());
expect(cancel).toBeFocused();
});
it('should cycle forward through textarea, tags, save, and cancel on TAB keydown', function() {
textArea.focus();
textArea.trigger(tabForwardEvent());
expect(tags).toBeFocused();
tags.trigger(tabForwardEvent());
expect(save).toBeFocused();
save.trigger(tabForwardEvent());
expect(cancel).toBeFocused();
cancel.trigger(tabForwardEvent());
expect(textArea).toBeFocused();
});
it('should cycle back through textarea, tags, save, and cancel on SHIFT + TAB keydown', function() {
textArea.focus();
textArea.trigger(tabBackwardEvent());
expect(cancel).toBeFocused();
cancel.trigger(tabBackwardEvent());
expect(save).toBeFocused();
save.trigger(tabBackwardEvent());
expect(tags).toBeFocused();
tags.trigger(tabBackwardEvent());
expect(textArea).toBeFocused();
});
it('should submit if target is Save on ENTER or SPACE keydown', function() {
save.focus();
save.trigger(keyDownEvent(this.KEY.ENTER));
expect(this.annotator.editor.submit).toHaveBeenCalled();
this.annotator.editor.submit.calls.reset();
save.focus();
save.trigger(keyDownEvent(this.KEY.SPACE));
expect(this.annotator.editor.submit).toHaveBeenCalled();
});
it('should submit on META or CONTROL + ENTER keydown', function() {
textArea.focus();
textArea.trigger(enterMetaKeyEvent());
expect(this.annotator.editor.submit).toHaveBeenCalled();
this.annotator.editor.submit.calls.reset();
textArea.focus();
textArea.trigger(enterControlKeyEvent());
expect(this.annotator.editor.submit).toHaveBeenCalled();
});
it('should hide if target is Cancel on ENTER or SPACE keydown', function() {
cancel.focus();
cancel.trigger(keyDownEvent(this.KEY.ENTER));
expect(this.annotator.editor.hide).toHaveBeenCalled();
this.annotator.editor.hide.calls.reset();
cancel.focus();
save.trigger(keyDownEvent(this.KEY.SPACE));
expect(this.annotator.editor.hide).toHaveBeenCalled();
});
it('should hide on ESCAPE keydown', function() {
var tabControls = [textArea, save, cancel];
_.each(tabControls, function(control) {
control.focus();
control.trigger(keyDownEvent(this.KEY.ESCAPE));
}, this);
expect(this.annotator.editor.hide.calls.count()).toBe(3);
});
});
});
});