Improve test coverage for the tutorial
Change-Id: Ibefcbcc019fee4acaa2f83ec3030e534509035ae
diff --git a/Changes b/Changes
index 32d216a..73d083d 100755
--- a/Changes
+++ b/Changes
@@ -1,4 +1,4 @@
-0.40 2020-10-13
+0.40 2020-10-20
- Modernize ES and fix in-loops.
- add roll() method to state object.
- Fix wrong hint-mirror behaviour in Firefox.
@@ -6,6 +6,7 @@
- Modernize ES and improve variable declarations.
- Improve JS test coverage for Datepicker.
- Fix character errors in hint helper at position 0.
+ - Improve JS test coverage for tutorial.
0.39 2020-10-07
- Add information on secret file to Readme.
diff --git a/dev/js/runner/all.html b/dev/js/runner/all.html
index 912fabd..99d4799 100644
--- a/dev/js/runner/all.html
+++ b/dev/js/runner/all.html
@@ -51,7 +51,8 @@
'spec/utilSpec',
'spec/stateSpec',
'spec/pipeSpec',
- 'spec/sessionSpec'
+ 'spec/sessionSpec',
+ 'spec/tutSpec'
],
function () {
window.onload();
diff --git a/dev/js/spec/tutSpec.js b/dev/js/spec/tutSpec.js
new file mode 100644
index 0000000..a37900c
--- /dev/null
+++ b/dev/js/spec/tutSpec.js
@@ -0,0 +1,294 @@
+define(['tutorial','util'], function (tutClass) {
+ describe('KorAP.Tutorial', function () {
+
+ const clean = function () {
+ ['tutorial','q-field','ql-field'].forEach(
+ function (id) {
+ const el = document.getElementById(id);
+ if (el != null) {
+ el.parentNode.removeChild(el);
+ };
+ }
+ );
+ };
+
+ beforeEach(clean);
+ afterEach(clean);
+
+ function queryFactory (query, ql = 'poliqarp', cutoff = true) {
+ const q = document.createElement('div');
+ q.setAttribute('data-query', query);
+ q.setAttribute('data-query-language', ql);
+ q.setAttribute('data-query-cutoff', cutoff);
+ return q;
+ };
+
+ it('should be initializable', function () {
+ tutObj = document.createElement('div');
+ let tut = tutClass.create(tutObj);
+ expect(tut).toBeFalsy();
+
+ tutObj = document.createElement('div');
+ tutObj.setAttribute('href', '/doc/ql');
+ tut = tutClass.create(tutObj);
+ expect(tut).toBeTruthy();
+ tut._session.clear();
+ });
+
+ it('should rewrite to JS open', function () {
+ tutObj = document.createElement('div');
+ tutObj.setAttribute('href', 'http://example.com');
+ expect(tutObj.getAttribute('href')).toBeTruthy();
+ expect(tutObj.onclick).toBeNull();
+ var tut = tutClass.create(tutObj);
+ expect(tutObj.getAttribute('href')).toBeFalsy();
+ expect(tutObj.onclick).not.toBeNull();
+ tut._session.clear();
+ });
+
+ it('should create an embedded tutorial', function () {
+ expect(document.getElementById('tutorial')).toBeFalsy();
+
+ tutObj = document.createElement('div');
+ tutObj.setAttribute('href', '/doc/ql');
+ let tut = tutClass.create(tutObj);
+
+ let tutEmb = document.getElementById('tutorial');
+ expect(tutEmb).toBeTruthy();
+
+ expect(tutEmb.style.display).toEqual('none');
+ tut._session.clear();
+ });
+
+ it('should be visible', function () {
+ const tutE = document.createElement('div');
+ tutE.setAttribute('href', '/doc/ql');
+ let tut = tutClass.create(tutE);
+ tut._session.clear();
+ let tutEmb = document.getElementById('tutorial');
+
+ expect(tutEmb.style.display).toEqual('none');
+ expect(tutEmb.getElementsByTagName('IFRAME')[0]).toBeUndefined();
+
+ tut.show();
+
+ expect(tutEmb.style.display).toEqual('block');
+
+ let iframe = tutEmb.getElementsByTagName('IFRAME')[0];
+ expect(iframe).not.toBeUndefined();
+ expect(iframe.getAttribute('src')).toEqual('/doc/ql?embedded=true');
+ tut._session.clear();
+ });
+
+ it('should be visible by click', function () {
+ const tutE = document.createElement('div');
+ tutE.setAttribute('href', '/doc/ql');
+ let tut = tutClass.create(tutE);
+ tut._session.clear();
+ let tutEmb = document.getElementById('tutorial');
+
+ expect(tutEmb.style.display).toEqual('none');
+ expect(tutEmb.getElementsByTagName('IFRAME')[0]).toBeUndefined();
+
+ tutE.click();
+
+ expect(tutEmb.style.display).toEqual('block');
+
+ let iframe = tutEmb.getElementsByTagName('IFRAME')[0];
+ expect(iframe).not.toBeUndefined();
+ expect(iframe.getAttribute('src')).toEqual('/doc/ql?embedded=true');
+ tut._session.clear();
+ });
+
+ it('should be hidable', function () {
+ const tutE = document.createElement('div');
+ tutE.setAttribute('href', '/doc/ql');
+ let tut = tutClass.create(tutE);
+ tut._session.clear();
+ let tutEmb = document.getElementById('tutorial');
+
+ tut.show();
+ expect(tutEmb.style.display).toEqual('block');
+
+ let iframe = tutEmb.getElementsByTagName('IFRAME')[0];
+ expect(iframe).not.toBeUndefined();
+
+ tut.hide();
+ expect(tutEmb.style.display).toEqual('none');
+
+ iframe = tutEmb.getElementsByTagName('IFRAME')[0];
+ expect(iframe).not.toBeUndefined();
+ tut._session.clear();
+ });
+
+ it('should remember page', function () {
+ const tutE = document.createElement('div');
+ tutE.setAttribute('href', '/doc/ql');
+ let tut = tutClass.create(tutE);
+ tut._session.clear();
+
+ expect(tut.getPage()).toBeUndefined();
+
+ tut.setPage('poliqarp');
+ expect(tut.getPage()).toEqual('poliqarp');
+
+ // Remember section
+ let sec = document.createElement('SECTION');
+ sec.setAttribute('id', 'cosmas-ii');
+
+ tut.setPage(sec);
+ expect(tut.getPage().endsWith('#cosmas-ii')).toBeTruthy();
+
+ // Check an inner obj
+ let doc = document.createElement('div');
+
+ // Create wrappers
+ let docP = document.createElement('div');
+ docP.appendChild(sec);
+ let pre = sec = sec.addE('pre');
+ sec = sec.addE('section');
+ sec.appendChild(doc);
+
+ expect(docP.outerHTML).toEqual(
+ '<div><section id="cosmas-ii"><pre><section><div>' +
+ '</div></section></pre></section></div>'
+ );
+
+ tut.setPage(doc);
+ expect(tut.getPage().endsWith('#cosmas-ii')).toBeTruthy();
+
+ pre.setAttribute('id', 'middle');
+
+ tut.setPage(doc);
+ expect(tut.getPage().endsWith('#middle')).toBeTruthy();
+ tut._session.clear();
+ });
+
+ it('should enable embedded queries', function () {
+
+ // qField
+ const qField = document.createElement('input');
+ qField.setAttribute('id','q-field');
+ qField.value = 'xxx';
+ document.body.appendChild(qField);
+
+ // qlSelect
+ const qlField = document.createElement('select');
+ qlField.setAttribute('id','ql-field');
+ let opt = qlField.addE('option');
+ opt.setAttribute('value', 'poliqarp');
+ opt = qlField.addE('option');
+ opt.setAttribute('value', 'cosmas-ii');
+ opt.selected = true;
+ document.body.appendChild(qlField);
+
+ expect(qlField.options[qlField.selectedIndex].value).toEqual('cosmas-ii');
+
+ const tutE = document.createElement('div');
+ tutE.setAttribute('href', '/doc/ql');
+
+ let tut = tutClass.create(tutE);
+ tut._session.clear();
+
+ let tutEmb = document.getElementById('tutorial');
+ expect(tutEmb).toBeTruthy();
+
+ expect(tutEmb.style.display).toEqual('none');
+
+ tut.show();
+
+ expect(tutEmb.style.display).toEqual('block');
+ expect(qField.value).toEqual("xxx");
+
+ let q = queryFactory("[orth=works]");
+ tut.useQuery(q);
+ expect(qField.value).toEqual("[orth=works]");
+ expect(qlField.options[qlField.selectedIndex].value).toEqual('poliqarp');
+ expect(tutEmb.style.display).toEqual('none');
+ tut._session.clear();
+ });
+
+ it('should initialize queries', function () {
+ const tutE = document.createElement('div');
+ tutE.setAttribute('href', '/doc/ql');
+ let tut = tutClass.create(tutE);
+ tut._session.clear();
+ let tutEmb = document.getElementById('tutorial');
+
+ let queries = document.createElement('div');
+
+ let pre0 = document.createElement('div');
+ queries.appendChild(pre0);
+
+ let pre1 = document.createElement('pre');
+ pre1.classList.add('query','tutorial');
+ queries.appendChild(pre1);
+
+ let pre2 = document.createElement('pre');
+ pre2.classList.add('query','tutorial','unsupported');
+ queries.appendChild(pre2);
+
+ let pre3 = document.createElement('div');
+ queries.appendChild(pre3);
+
+ let pre4 = document.createElement('pre');
+ pre4.classList.add('query','tutorial');
+ queries.appendChild(pre4);
+
+ expect(pre0.onclick).toBeNull();
+ expect(pre1.onclick).toBeNull();
+ expect(pre2.onclick).toBeNull();
+ expect(pre3.onclick).toBeNull();
+ expect(pre4.onclick).toBeNull();
+
+ tut.initQueries(queries);
+
+ expect(pre0.onclick).toBeNull();
+ expect(pre1.onclick).not.toBeNull();
+ expect(pre2.onclick).toBeNull();
+ expect(pre3.onclick).toBeNull();
+ expect(pre4.onclick).not.toBeNull();
+ tut._session.clear();
+ });
+
+ it('should initialize doc-links', function () {
+ const tutE = document.createElement('div');
+ tutE.setAttribute('href', '/doc/ql');
+ let tut = tutClass.create(tutE);
+ tut._session.clear();
+ let tutEmb = document.getElementById('tutorial');
+
+ let docLinks = document.createElement('div');
+
+ let a;
+ let l1 = a = docLinks.addE('a');
+ a.setAttribute('href','example:1');
+ let l2 = a = docLinks.addE('a');
+ a.setAttribute('href','example:2');
+ a.classList.add('doc-link');
+ let l3 = a = docLinks.addE('a');
+ a.setAttribute('href','example:3');
+ let l4 = a = docLinks.addE('a');
+ a.setAttribute('href','example:4');
+ a.classList.add('doc-link');
+
+ expect(l1.onclick).toBeNull();
+ expect(l2.onclick).toBeNull();
+ expect(l3.onclick).toBeNull();
+ expect(l4.onclick).toBeNull();
+
+ tut.initDocLinks(docLinks);
+
+ expect(l1.onclick).toBeNull();
+ expect(l2.onclick).not.toBeNull();
+ expect(l3.onclick).toBeNull();
+ expect(l4.onclick).not.toBeNull();
+
+ // Click
+ expect(tut.getPage()).toEqual(undefined);
+ l2.onclick();
+ expect(tut.getPage()).toEqual('example:2');
+ tut._session.clear();
+ });
+ });
+});
diff --git a/dev/js/src/tutorial.js b/dev/js/src/tutorial.js
index c2f7c69..ee311dd 100644
--- a/dev/js/src/tutorial.js
+++ b/dev/js/src/tutorial.js
@@ -6,14 +6,18 @@
// TODO: Add query mechanism!
// TODO: Highlight current section:
// http://stackoverflow.com/questions/24887258/highlight-navigation-link-as-i-scroll-down-the-page
+"use strict";
+
define(['session','buttongroup','util'], function (sessionClass, buttonGroupClass) {
- "use strict";
// Localization values
const loc = KorAP.Locale;
loc.CLOSE = loc.CLOSE || 'Close';
+ const d = document;
+
return {
+
/**
* Create new tutorial object.
* Accepts an element to bind the tutorial window to.
@@ -24,41 +28,48 @@
return Object.create(this)._init(obj,session);
},
+
// Initialize Tutorial object
_init : function (obj, session) {
+ const t = this;
if (session === undefined) {
- this._session = sessionClass.create();
+ t._session = sessionClass.create();
}
else {
- this._session = session;
+ t._session = session;
};
-
if (obj) {
- this._show = obj;
- this.start = obj.getAttribute('href');
+ t._show = obj;
+ t.start = obj.getAttribute('href');
+
+ // Unknown which tutorial to show
+ if (!t.start)
+ return null;
+
obj.removeAttribute('href');
- var that = this;
+
obj.onclick = function () {
- that.show();
- };
+ this.show();
+ }.bind(t);
// Injects a tutorial div to the body
- var div = document.createElement('div');
+ const div = d.createElement('div');
div.setAttribute('id', 'tutorial');
div.style.display = 'none';
- document.getElementsByTagName('body')[0].appendChild(div);
- this._iframe = null;
- this._element = div;
+ d.getElementsByTagName('body')[0].appendChild(div);
+
+ t._iframe = null;
+ t._element = div;
// Some fields
- this._ql = document.getElementById("ql-field");
- this._q = document.getElementById("q-field")
- this._cutoff = document.getElementById("q-cutoff-field");
+ t._ql = d.getElementById("ql-field");
+ t._q = d.getElementById("q-field")
+ t._cutoff = d.getElementById("q-cutoff-field");
};
- return this;
+ return t;
},
@@ -66,41 +77,43 @@
* Initialize a search with a defined query.
*/
useQuery : function (e) {
- var q = e.getAttribute("data-query");
- var ql = e.getAttribute("data-query-language");
- var qc = e.getAttribute("data-query-cutoff");
+ const t = this;
+ const q = e.getAttribute("data-query"),
+ ql = e.getAttribute("data-query-language"),
+ qc = e.getAttribute("data-query-cutoff");
+
if (qc !== 0 && qc !== "0" && qc !== "off" && qc !== null) {
- this._cutoff.checked = true;
+ if (t._cuttoff)
+ t._cutoff.checked = true;
};
if (KorAP.QLmenu) {
KorAP.QLmenu.selectValue(ql);
- };
- /*
- var qlf = this._ql.options;
- for (var i in qlf) {
- if (qlf[i].value == ql) {
- qlf[i].selected = true;
- break;
- };
- };
- */
+ }
- this._q.value = q;
- this.setPage(e);
- this.hide();
+ else if (t._ql) {
+ let found = Array.from(t._ql.options).find(o => o.value === ql);
+ if (found)
+ found.selected = true;
+ };
+
+ if (t._q)
+ t._q.value = q;
+
+ t.setPage(e);
+ t.hide();
},
+
/**
* Decorate a page with query event handler.
*/
initQueries : function (d) {
- let that = this;
Array.from(d.querySelectorAll('pre.query.tutorial:not(.unsupported)')).forEach(
i =>
i.onclick = function (e) {
- that.useQuery(this,e);
- }
+ this.useQuery(this,e);
+ }.bind(this)
);
},
@@ -108,14 +121,14 @@
* Decorate a page with documentation links
*/
initDocLinks : function (d) {
- let that = this;
+ const that = this;
Array.from(d.getElementsByClassName('doc-link')).forEach(
i =>
- i.onclick = function (e) {
+ i.onclick = function () {
that.setPage(this.getAttribute('href'));
return true;
}
- );
+ );
},
@@ -123,25 +136,26 @@
* Show the tutorial page embedded.
*/
show : function () {
- var element = this._element;
+ const t = this;
+ const element = t._element;
if (element.style.display === 'block')
return;
- if (this._iframe === null) {
- this._iframe = document.createElement('iframe');
- this._iframe.setAttribute(
+ if (t._iframe === null) {
+ t._iframe = d.createElement('iframe');
+ t._iframe.setAttribute(
'src',
- (this.getPage() || this.start) + '?embedded=true'
+ (t.getPage() || t.start) + '?embedded=true'
);
- var btn = buttonGroupClass.create(
+ const btn = buttonGroupClass.create(
['action','button-view']
);
- var that = this;
btn.add(loc.CLOSE, {'cls':['button-icon','close']}, function () {
element.style.display = 'none';
});
+
element.appendChild(btn.element());
// Add open in new window button
@@ -152,19 +166,17 @@
.appendChild(document.createTextNode(loc.SHOWINFO));
info.classList.add('info');
info.setAttribute('title', loc.SHOWINFO);
- */
- /*
- ul.appendChild(close);
-
- element.appendChild(ul);
+ ul.appendChild(close);
+ element.appendChild(ul);
*/
- element.appendChild(this._iframe);
+ element.appendChild(t._iframe);
};
element.style.display = 'block';
},
+
/**
* Close tutorial window.
*/
@@ -172,34 +184,36 @@
this._element.style.display = 'none';
},
+
/**
* Set a page to be the current tutorial page.
* Expects either a string or an element.
*/
setPage : function (obj) {
- var page = obj;
+ let page = obj;
+
if (typeof page != 'string') {
- var l = this._iframe !== null ? window.frames[0].location : window.location;
+ const l = this._iframe !== null ? window.frames[0].location : window.location;
+
page = l.pathname + l.search;
- for (var i = 1; i < 5; i++) {
- if (obj.nodeName === 'SECTION') {
- if (obj.hasAttribute('id'))
- page += '#' + obj.getAttribute('id');
- break;
- }
- else if (obj.nodeName === 'PRE' && obj.hasAttribute('id')) {
+ for (let i = 1; i < 5; i++) {
+ if ((obj.nodeName === 'SECTION' || obj.nodeName === 'PRE') && obj.hasAttribute('id')) {
page += '#' + obj.getAttribute('id');
break;
}
else {
obj = obj.parentNode;
+ if (obj === null)
+ break;
};
};
};
+
this._session.set('tutpage', page);
},
+
/**
* Get the current tutorial URL
*/