diff --git a/.jscsrc b/.jscsrc new file mode 100644 index 0000000..576f754 --- /dev/null +++ b/.jscsrc @@ -0,0 +1,5 @@ +{ + "preset": "jquery", + // Since we check quotemarks already in jshint, this can be turned off + "validateQuoteMarks": false +} diff --git a/karma.conf.js b/karma.conf.js index 4844c94..4d42e54 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -7,11 +7,26 @@ module.exports = function( config ) { // Frameworks to use frameworks: [ "qunit" ], + proxies : { + '/test/' : '/base/test/', + '/js/' : '/base/js/', + '/node_modules/syn/dist/' : '/base/node_modules/syn/dist/' + }, + // List of files / patterns to load in the browser files: [ - "test/bootstrap.js", - "js/impress.js", - "test/core_tests.js" + // The QUnit tests + "test/helpers.js", + "test/core_tests.js", + "test/navigation_tests.js", + // Presentation files, for the iframe + //"test/core_tests_presentation.html" + //{pattern: "test/core_tests_presentation.html", watched: true, served: true, included: false} + {pattern: "test/*.html", watched: true, served: true, included: false}, + {pattern: "test/plugins/*/*.html", watched: true, served: true, included: false}, + // JS files for iframe + {pattern: "js/impress.js", watched: true, served: true, included: false}, + {pattern: "node_modules/syn/dist/global/syn.js", watched: false, served: true, included: false} ], // List of files to exclude @@ -43,11 +58,22 @@ module.exports = function( config ) { // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`) // - PhantomJS // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`) - browsers: [ "Chrome" ], + //browsers: [ "Chrome" ], + //browsers: [ "Firefox" ], + browsers: [ "Chrome", "Firefox" ], - // If browser does not capture in given timeout [ms], kill it - captureTimeout: 60000, + client: { + clearContext: false, + qunit: { + showUI: true, + testTimeout: 120*1000 + } + }, + // If browser does not capture, or produce output, in given timeout [ms], kill it + captureTimeout: 60*1000, + browserNoActivityTimeout: 60*1000, + // Continuous Integration mode // if true, it capture browsers, run tests and exit singleRun: false diff --git a/package.json b/package.json index 34c40a7..1b377eb 100644 --- a/package.json +++ b/package.json @@ -22,20 +22,23 @@ "url": "https://github.com/bartaz/impress.js/issues" }, "scripts": { - "lint": "jshint js/impress.js *.js test/bootstrap.js && jscs js/impress.js *.js test/bootstrap.js --preset=jquery", + "lint": "jshint js/impress.js test/*.js && jscs js/impress.js test/*.js", "test": "karma start --single-run", "test:dev": "karma start", "test:sauce": "karma start karma.conf-sauce.js" }, "devDependencies": { "chrome": "0.1.0", + "firefox": "0.0.1", "jscs": "2.11.0", "jshint": "2.9.1", "karma": "0.13.22", "karma-chrome-launcher": "1.0.1", "karma-cli": "1.0.0", + "karma-firefox-launcher": "~0.1", "karma-qunit": "1.0.0", "karma-sauce-launcher": "1.0.0", - "qunitjs": "2.0.0-rc1" + "qunitjs": "2.0.0-rc1", + "syn": "0.10.0" } } diff --git a/qunit_test_runner.html b/qunit_test_runner.html new file mode 100644 index 0000000..92f24d6 --- /dev/null +++ b/qunit_test_runner.html @@ -0,0 +1,26 @@ + + + + + + QUnit tests for impress.js + + + +
+ +
+ + + + + + + + + + + diff --git a/test/bootstrap.js b/test/bootstrap.js index 6a315e8..774d62f 100644 --- a/test/bootstrap.js +++ b/test/bootstrap.js @@ -1,12 +1,25 @@ /*jshint browser:true */ -var root = document.createElement( "div" ); -root.innerHTML = [ - "
", - "
First slide
", - "
Second slide
", - "
Third slide
", - "
Fourth slide
", - "
" -].join( "" ); -document.body.appendChild( root ); +// TODO: This is the bootstrap file for *karma*. Poorly named (since karma is +// only one option, in this repo) but keeping the same name now to avoid +// unnecessary deviation with upstream. +// If you just want to run the tests locally, you can open test/index.html in Firefox. + +// That's annoying: karma-qunit doesn't provide the qunit-fixture element +// https://github.com/karma-runner/karma-qunit/issues/18 + +// This file contains so much HTML, that we will just respectfully disagree about js +/* jshint quotmark:single */ +/* global document */ + +var fix = document.createElement( 'div' ); +fix.id = 'qunit-fixture'; +fix.innerHTML = [ +'\n', +' ' +].join( '' ); diff --git a/test/core_tests.js b/test/core_tests.js index 12d2378..5a61e74 100644 --- a/test/core_tests.js +++ b/test/core_tests.js @@ -1,166 +1,266 @@ -(function() { - var registerEventListener = function( target, eventType, callback ) { - target.addEventListener( eventType, callback ); - return function removeRegisteredEventListener() { - target.removeEventListener( eventType, callback ); +/* + * Copyright 2016 Henrik Ingo (@henrikingo) + * + * Released under the MIT license. See LICENSE file. + */ + +/* global document, console, setTimeout, loadIframe, initPresentation, _impressSupported, QUnit */ + +QUnit.module( "Core Tests" ); + +QUnit.test( "Initialize Impress.js", function( assert ) { + console.log( "Begin init() test" ); + + // Init triggers impress:init and impress:stepenter events, which we want to catch. + var doneInit = assert.async(); + var doneStepEnter = assert.async(); + var doneSync = assert.async(); + + loadIframe( "test/core_tests_presentation.html", assert, function() { + var iframe = document.getElementById( "presentation-iframe" ); + var iframeDoc = iframe.contentDocument; + var iframeWin = iframe.contentWindow; + var root = iframeDoc.querySelector( "div#impress" ); + + // Catch events triggered by init() + var assertInit = function() { + assert.ok( true, "impress:init event triggered." ); + + var canvas = iframeDoc.querySelector( "div#impress > div" ); + + // Delay and duration don't become set before the first transition actually happened + assert.equal( canvas.style.transitionDelay, + "0ms", + "canvas.style.transitionDelay initialized correctly" ); + assert.equal( canvas.style.transitionDuration, + "0ms", + "canvas.style.transitionDuration initialized correctly" ); + + doneInit(); + console.log( "End init() test (async)" ); }; - }; - QUnit.begin(function() { - impress().init(); - }); + var assertInitWrapper = function() { + setTimeout( function() { assertInit(); }, 10 ); + }; + root.addEventListener( "impress:init", assertInitWrapper ); - QUnit.module( "Initialization" ); + root.addEventListener( "impress:stepenter", function( event ) { + assert.ok( true, "impress:stepenter event triggered." ); + var step1 = iframeDoc.querySelector( "div#step-1" ); + assert.equal( event.target, step1, + event.target.id + " triggered impress:stepenter event." ); + doneStepEnter(); + } ); - QUnit.test( "Global Scope", function( assert ) { - assert.expect( 1 ); - assert.ok( impress, "impress declared in global scope" ); - }); + // Synchronous code and assertions + assert.ok( iframeWin.impress, + "impress declared in global scope" ); + assert.strictEqual( iframeWin.impress().init(), undefined, + "impress().init() called." ); + assert.strictEqual( iframeWin.impress().init(), undefined, + "It's ok to call impress().init() a second time, it's a no-op." ); - QUnit.test( "Multiple API instantiation", function( assert ) { - assert.expect( 0 ); - impress().init(); - }); + // The asserts below are true immediately after impress().init() returns. + // Therefore we test them here, not in an event handler. + var notSupportedClass = iframeDoc.body.classList.contains( "impress-not-supported" ); + var yesSupportedClass = iframeDoc.body.classList.contains( "impress-supported" ); + if ( !_impressSupported() ) { + assert.ok( notSupportedClass, + "body.impress-not-supported class still there." ); + assert.ok( !yesSupportedClass, + "body.impress-supported class was NOT added." ); + } else { + assert.ok( !notSupportedClass, + "body.impress-not-supported class was removed." ); + assert.ok( yesSupportedClass, + "body.impress-supported class was added." ); - QUnit.test( "Support Markup", function( assert ) { - assert.expect( 4 ); + assert.ok( !iframeDoc.body.classList.contains( "impress-disabled" ), + "body.impress-disabled is removed." ); + assert.ok( iframeDoc.body.classList.contains( "impress-enabled" ), + "body.impress-enabled is added." ); - var impressNotSupported = document.body.classList.contains( "impress-not-supported" ); - var impressSupported = document.body.classList.contains( "impress-supported" ); - assert.ok( impressSupported, "Have class .impress-supported" ); - assert.notOk( impressNotSupported, "Don't have class .impress-not-supported" ); + var canvas = iframeDoc.querySelector( "div#impress > div" ); + assert.ok( !canvas.classList.contains( "step" ) && canvas.id === "", + "Additional 'canvas' div inserted between div#impress root and steps." ); + assert.equal( canvas.style.transform, + "rotateZ(0deg) rotateY(0deg) rotateX(0deg) translate3d(1000px, 0px, 0px)", + "canvas.style.transform initialized correctly" ); + assert.equal( canvas.style.transformOrigin, + "left top 0px", + "canvas.style.transformOrigin initialized correctly" ); + assert.equal( canvas.style.transformStyle, + "preserve-3d", + "canvas.style.transformStyle initialized correctly" ); + assert.equal( canvas.style.transitionProperty, + "all", + "canvas.style.transitionProperty initialized correctly" ); + assert.equal( canvas.style.transitionTimingFunction, + "ease-in-out", + "canvas.style.transitionTimingFunction initialized correctly" ); - var impressDisabled = document.body.classList.contains( "impress-disabled" ); - var impressEnabled = document.body.classList.contains( "impress-enabled" ); - assert.ok( impressEnabled, "Have class .impress-enabled" ); - assert.notOk( impressDisabled, "Don't have class .impress-disabled" ); - }); + assert.equal( iframeDoc.documentElement.style.height, + "100%", + "documentElement.style.height is 100%" ); - QUnit.test( "Attributes", function( assert ) { - assert.expect( 10 ); + // Steps initialization + var step1 = iframeDoc.querySelector( "div#step-1" ); + assert.equal( step1.style.position, + "absolute", + "Step position is 'absolute'." ); - var actual, expected; - var root = document.querySelector( "#impress" ); - var canvas = document.querySelector( "div#impress > div" ); + assert.ok( step1.classList.contains( "active" ), + "Step 1 has active css class." ); - var canvasIsNotAStep = !canvas.classList.contains("step") && canvas.id === ""; - assert.ok( canvasIsNotAStep, "Canvas do not have step element data" ); + } + doneSync(); + console.log( "End init() test (sync)" ); + } ); // LoadIframe() - actual = canvas.style.webkitTransform || canvas.style.transform; - expected = "rotateZ(0deg) rotateY(0deg) rotateX(0deg) translate3d(1000px, 0px, 0px)"; - assert.strictEqual( actual, expected, "canvas.style.transform initialized correctly" ); +} ); - // Normalize result for IE 11 and Safari. - actual = canvas.style.webkitTransformOrigin || canvas.style.transformOrigin; - expected = "left top 0px"; +// Note: Here we focus on testing the core functionality of moving between +// steps, the css classes set and unset, and events triggered. +// TODO: more complex animations and check position, transitions, delays, etc... +QUnit.test( "Impress Core API", function( assert ) { + console.log( "Begin core api test" ); + var done = assert.async(); + loadIframe( "test/core_tests_presentation.html", assert, function() { + initPresentation( assert, function() { + var iframe = document.getElementById( "presentation-iframe" ); + var iframeDoc = iframe.contentDocument; + var iframeWin = iframe.contentWindow; - if ( actual === "left top" || actual === "0% 0%" ) { - actual = expected; - } - assert.strictEqual( actual, expected, "canvas.style.transformOrigin initialized correctly" ); + // Impress.js itself uses event listeners to manipulate most CSS classes. + // Wait a short while before checking, to avoid race. + // (See assertStepEnterWrapper and assertStepLeaveWrapper.) + var wait = 5; // Milliseconds - actual = canvas.style.webkitTransformStyle || canvas.style.transformStyle; - expected = "preserve-3d"; - assert.strictEqual( actual, expected, "canvas.style.transformStyle initialized correctly" ); + var step1 = iframeDoc.querySelector( "div#step-1" ); + var step2 = iframeDoc.querySelector( "div#step-2" ); + var step3 = iframeDoc.querySelector( "div#step-3" ); + var step4 = iframeDoc.querySelector( "div#fourth" ); + var root = iframeDoc.querySelector( "div#impress" ); - actual = canvas.style.transitionDelay; - expected = "0ms"; - assert.strictEqual( actual, expected, "canvas.style.transitionDelay initialized correctly" ); + // On impress:stepenter, we do some assertions on the "entered" object. + // On impress:stepleave, we do some assertions on the "left" object. + // Finally we call next() to initialize the next transition, and it starts all over again. + var i = 0; + var sequence = [ { left: step1, + entered: step2, + next: function() { return iframeWin.impress().goto( 2 ); }, + text: "goto() called and returns ok (2->3)" }, + { left: step2, + entered: step3, + next: function() { return iframeWin.impress().goto( "fourth" ); }, + text: "goto() called and returns ok (3->4)" }, + { left: step3, + entered: step4, + next: function() { return iframeWin.impress().next(); }, + text: "next() wraps around to first step (4->1)" }, + { left: step4, + entered: step1, + next: function() { return iframeWin.impress().prev(); }, + text: "prev() wraps around to last step (1->4)" }, + { left: step1, + entered: step4, + next: function() { return iframeWin.impress().prev(); }, + text: "prev() called and returns ok (4->3)" }, + { left: step4, + entered: step3, + next: function() { return iframeWin.impress().goto( 0 ); }, + text: "End of test suite, return to first step with goto(0)." }, + { left: step3, + entered: step1, + next: false } // False = end of sequence + ]; - actual = canvas.style.transitionDuration; - expected = "0ms"; - assert.strictEqual( actual, expected, "canvas.style.transitionDuration initialized correctly" ); + // When both assertStepEnter and assertStepLeave are done, we can go to next step in sequence. + var readyCount = 0; + var readyForNext = function() { + readyCount++; + if ( readyCount % 2 === 0 ) { + if ( sequence[ i ].next ) { + assert.ok( sequence[ i ].next(), sequence[ i ].text ); + i++; + assertImmediately(); + } else { + done(); + console.log( "End core api test" ); + } + } + }; - actual = canvas.style.transitionProperty; - expected = "all"; - assert.strictEqual( actual, expected, "canvas.style.transitionProperty initialized correctly" ); + // Things to check on impress:stepenter event -----------------------------// + var assertStepEnter = function( event ) { + assert.equal( event.target, sequence[ i ].entered, + event.target.id + " triggered impress:stepenter event." ); + assert.ok( event.target.classList.contains( "present" ), + event.target.id + " set present css class." ); + assert.ok( !event.target.classList.contains( "future" ), + event.target.id + " unset future css class." ); + assert.ok( !event.target.classList.contains( "past" ), + event.target.id + " unset past css class." ); + assert.equal( "#/" + event.target.id, iframeWin.location.hash, + "Hash is " + "#/" + event.target.id ); - actual = canvas.style.transitionTimingFunction; - expected = "ease-in-out"; - assert.strictEqual( actual, expected, "canvas.style.transitionTimingFunction initialized correctly" ); + // Just by way of comparison, check transitionDuration again, in a non-init transition + var canvas = iframeDoc.querySelector( "div#impress > div" ); + assert.equal( canvas.style.transitionDelay, + "0ms", + "canvas.style.transitionDelay set correctly" ); + assert.equal( canvas.style.transitionDuration, + "1000ms", + "canvas.style.transitionDuration set correctly" ); - actual = root.style.perspective; - expected = ""; - assert.notStrictEqual( actual, expected, "root.style.perspective should be set explicitly for IE 11" ); + readyForNext(); + }; - actual = document.documentElement.style.height; - expected = "100%"; - assert.strictEqual( actual, expected, "documentElement.style.height is 100%" ); - }); + var assertStepEnterWrapper = function( event ) { + setTimeout( function() { assertStepEnter( event ); }, wait ); + }; + root.addEventListener( "impress:stepenter", assertStepEnterWrapper ); - QUnit.test( "Steps", function( assert ) { - assert.expect( 2 ); + // Things to check on impress:stepleave event -----------------------------// + var assertStepLeave = function( event ) { + assert.equal( event.target, sequence[ i ].left, + event.target.id + " triggered impress:stepleave event." ); + assert.ok( !event.target.classList.contains( "present" ), + event.target.id + " unset present css class." ); + assert.ok( !event.target.classList.contains( "future" ), + event.target.id + " unset future css class." ); + assert.ok( event.target.classList.contains( "past" ), + event.target.id + " set past css class." ); + readyForNext(); + }; - var actual, expected; - var step1 = document.querySelector( "div#step-1" ); + var assertStepLeaveWrapper = function( event ) { + setTimeout( function() { assertStepLeave( event ); }, wait ); + }; + root.addEventListener( "impress:stepleave", assertStepLeaveWrapper ); - actual = step1.style.position; - expected = "absolute"; - assert.strictEqual( actual, expected, "Step position is 'absolute'" ); + // Things to check immediately after impress().goto() ---------------------------// + var assertImmediately = function() { + assert.ok( sequence[ i ].entered.classList.contains( "active" ), + sequence[ i ].entered.id + " set active css class." ); + assert.ok( !sequence[ i ].left.classList.contains( "active" ), + sequence[ i ].left.id + " unset active css class." ); + }; - assert.ok( step1.classList.contains( "active" ), "Step 1 has active css class." ); - }); + // Done with setup. Start testing! -----------------------------------------------// + // Do no-op tests first, then trigger the sequence of transitions we setup above. // - QUnit.module( "Core API" ); + assert.strictEqual( iframeWin.impress().goto( iframeDoc.querySelector( "div#impress" ) ), + false, + "goto() to a non-step element fails, as it should." ); + assert.strictEqual( iframeWin.impress().goto(), + false, + "goto() fails, as it should." ); - QUnit.test( ".next()", function( assert ) { - assert.expect( 2 ); - - var root = document.querySelector( "div#impress" ); - var step1 = document.querySelector( "div#step-1" ); - var step2 = document.querySelector( "div#step-2" ); - var done = assert.async(); - - impress().next(); - - assert.ok( step2.classList.contains( "active" ), step2.id + " add active css class." ); - assert.notOk( step1.classList.contains( "active" ), step1.id + " remove active css class." ); - - // Reset to original state - var removeStepEnterEvent = registerEventListener( root, "impress:stepenter", function() { - removeStepEnterEvent(); - done(); - }); - impress().goto( step1 ); - }); - - QUnit.test( "impress:stepenter event", function( assert ) { - assert.expect( 4 ); - - var actual, expected; - var root = document.querySelector( "div#impress" ); - var step1 = document.querySelector( "div#step-1" ); - var step2 = document.querySelector( "div#step-2" ); - var done = assert.async(); - - var removeTestEvent = registerEventListener( root, "impress:stepenter", function( event ) { - actual = event.target; - expected = step2; - assert.strictEqual( actual, expected, "Triggered event for the second step" ); - - assert.ok( step2.classList.contains( "present" ), event.target.id + " add present css class" ); - assert.notOk( step2.classList.contains( "future" ), event.target.id + " remove future css class" ); - assert.notOk( step2.classList.contains( "past" ), event.target.id + " remove active css class." ); - - removeTestEvent(); - - // Reset to original state - var removeCleanupEvent = registerEventListener( root, "impress:stepenter", function() { - removeCleanupEvent(); - done(); - }); - - impress().goto( step1 ); - }); - - impress().next(); - }); - - QUnit.done(function( details ) { - // Impress.js will set the hash part of the url, we want to unset it when finished - // Otherwise a refresh of browser page would not start tests from the last step step - window.location.hash = ""; - // Add back vertical scrollbar so we can read results if there were failures. - document.body.style.overflow = 'auto'; - }); -}()); + // This starts executing the sequence above + assert.ok( iframeWin.impress().next(), + "next() called and returns ok (1->2)" ); + } ); // InitPresentation() + } ); // LoadIframe() +} ); diff --git a/test/core_tests_presentation.html b/test/core_tests_presentation.html new file mode 100644 index 0000000..34ebb9f --- /dev/null +++ b/test/core_tests_presentation.html @@ -0,0 +1,24 @@ + + + + + + The presentation steps used in an iframe in core_tests.js + + + +
+
First slide
+ + +
Fourth slide
+
+ + + + + + diff --git a/test/helpers.js b/test/helpers.js new file mode 100644 index 0000000..746facd --- /dev/null +++ b/test/helpers.js @@ -0,0 +1,110 @@ +// This file contains so much HTML, that we will just respectfully disagree about js +/* jshint quotmark:single */ +/* global document, console, setTimeout, navigator */ +/* exported loadIframe, initPresentation, _impressSupported */ + +var loadIframe = function( src, assert, callback ) { + console.log( 'Begin loadIframe' ); + + // When running in Karma, the #qunit-fixture appears from somewhere and we can't set its + // contents in advance. + var fix = document.getElementById( 'qunit-fixture' ); + fix.innerHTML = [ + '\n', + ' ' + ].join( '' ); + + var iframe = document.getElementById( 'presentation-iframe' ); + + var onLoad = function() { + assert.ok( true, + 'Presentation loaded. iframe.src = ' + iframe.src ); + try { + assert.ok( iframe.contentDocument, + 'Verifying that tests can access the presentation inside the iframe. ' + + 'Note: On Firefox this fails when using paths with "../" parts for the iframe.' ); + } + catch ( err ) { + assert.ok( false, + 'Error when trying to access presentation in iframe. Note: When using Chrome with ' + + 'local files (file:///) this will fail with SecurityError. ' + + 'You can however use Chrome over Karma.' ); + } + console.log( 'End loadIframe' ); + callback(); + }; + + // FIXME: Seems to be some race in loading js files inside the iframe (in CircleCI). + // The error that happens is that window.impress isn't set yet, even if onLoad event triggered. + // Needs more investigation. + var onLoadWrapper = function( event ) { + setTimeout( function() { onLoad( event ); }, 1000 ); + }; + iframe.addEventListener( 'load', onLoadWrapper ); + + assert.ok( true, + 'Setting iframe.src = ' + src ); + iframe.src = src; +}; + +var initPresentation = function( assert, callback ) { + console.log( 'Begin initPresentation' ); + var iframe = document.getElementById( 'presentation-iframe' ); + var iframeDoc = iframe.contentDocument; + var iframeWin = iframe.contentWindow; + + // Impress:stepenter is the last event triggered in init(), so we wait for that. + var waitForStepEnter = function( event ) { + assert.ok( true, 'impress (' + event.target.id + ') is now initialized.' ); + iframeDoc.removeEventListener( 'impress:stepenter', waitForStepEnterWrapper ); + console.log( 'End initPresentation' ); + callback(); + }; + + // Unfortunately, impress.js uses the impress:stepenter event internally to + // do some things related to entering a step. This causes a race condition when + // we listen for the same event and expect it to be done with everything. + // We wait 5 ms to resolve the race condition, then it's safe to start testing. + var waitForStepEnterWrapper = function( event ) { + setTimeout( function() { waitForStepEnter( event ); }, 5 ); + }; + iframeDoc.addEventListener( 'impress:stepenter', waitForStepEnterWrapper ); + + assert.strictEqual( iframeWin.impress().init(), undefined, 'Initializing impress.' ); +}; + +// Helper function to determine whether this browser is supported by +// impress.js or not. Copied from impress.js itself. +var _impressSupported = function() { + var pfx = ( function() { + var style = document.createElement( 'dummy' ).style, + prefixes = 'Webkit Moz O ms Khtml'.split( ' ' ), + memory = {}; + return function( prop ) { + if ( typeof memory[ prop ] === 'undefined' ) { + var ucProp = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ), + props = ( prop + ' ' + prefixes.join( ucProp + ' ' ) + ucProp ).split( ' ' ); + memory[ prop ] = null; + for ( var i in props ) { + if ( style[ props[ i ] ] !== undefined ) { + memory[ prop ] = props[ i ]; + break; + } + } + } + return memory[ prop ]; + }; + } )(); + + var ua = navigator.userAgent.toLowerCase(); + return ( pfx( 'perspective' ) !== null ) && + ( document.body.classList ) && + ( document.body.dataset ) && + ( ua.search( /(iphone)|(ipod)|(android)/ ) === -1 ); +}; + diff --git a/test/navigation_tests.js b/test/navigation_tests.js new file mode 100644 index 0000000..fd4a05b --- /dev/null +++ b/test/navigation_tests.js @@ -0,0 +1,169 @@ +/* + * Copyright 2016 Henrik Ingo (@henrikingo) + * + * Released under the MIT license. See LICENSE file. + */ +/* global QUnit, loadIframe, initPresentation, document, window */ + +QUnit.module( "Navigation plugin" ); + +QUnit.test( "Navigation Plugin", function( assert ) { + window.console.log( "Begin navigation plugin" ); + var done = assert.async(); + + loadIframe( "test/core_tests_presentation.html", assert, function() { + initPresentation( assert, function() { + var iframe = document.getElementById( "presentation-iframe" ); + var iframeDoc = iframe.contentDocument; + var iframeWin = iframe.contentWindow; + + var wait = 5; // Milliseconds + + var step1 = iframeDoc.querySelector( "div#step-1" ); + var step2 = iframeDoc.querySelector( "div#step-2" ); + var step3 = iframeDoc.querySelector( "div#step-3" ); + var step4 = iframeDoc.querySelector( "div#fourth" ); + var root = iframeDoc.querySelector( "div#impress" ); + + var i = 0; + var sequence = [ { left: step1, + entered: step2, + next: function() { return iframeWin.syn.type( "bodyid", " " ); }, + text: "space (2->3)" }, + { left: step2, + entered: step3, + next: function() { return iframeWin.syn.type( "bodyid", "[right]" ); }, + text: "[right] (3->4)" }, + { left: step3, + entered: step4, + next: function() { return iframeWin.syn.type( "bodyid", "\t" ); }, + text: "tab (4->1)" }, + { left: step4, + entered: step1, + next: function() { return iframeWin.syn.type( "bodyid", "[down]" ); }, + text: "[down] (1->2)" }, + { left: step1, + entered: step2, + next: function() { return iframeWin.syn.type( "bodyid", "[page-down]" ); }, + text: "[page-down] (2->3)" }, + { left: step2, + entered: step3, + next: function() { return iframeWin.syn.type( "bodyid", "[page-up]" ); }, + text: "[page-up] (3->2)" }, + { left: step3, + entered: step2, + next: function() { return iframeWin.syn.type( "bodyid", "[left]" ); }, + text: "[left] (2->1)" }, + { left: step2, + entered: step1, + next: function() { return iframeWin.syn.type( "bodyid", "[up]" ); }, + text: "[up] (1->4)" }, + { left: step1, + entered: step4, + next: function() { return iframeWin.syn.click( "step-2", {} ); }, + text: "click on 2 (4->2)" }, + { left: step4, + entered: step2, + next: function() { return iframeWin.syn.click( "linktofourth", {} ); }, + text: "click on link with href to id=fourth (2->4)" }, + { left: step2, + entered: step4, + next: function() { return iframeWin.impress().goto( 0 ); }, + text: "Return to first step with goto(0)." }, + { left: step4, + entered: step1, + next: false } + ]; + + var readyCount = 0; + var readyForNext = function() { + readyCount++; + if ( readyCount % 2 === 0 ) { + if ( sequence[ i ].next ) { + assert.ok( sequence[ i ].next(), sequence[ i ].text ); + i++; + } else { + window.console.log( "End navigation plugin" ); + done(); + } + } + }; + + // Things to check on impress:stepenter event -----------------------------// + var assertStepEnter = function( event ) { + assert.equal( event.target, sequence[ i ].entered, + event.target.id + " triggered impress:stepenter event." ); + readyForNext(); + }; + + var assertStepEnterWrapper = function( event ) { + window.setTimeout( function() { assertStepEnter( event ); }, wait ); + }; + root.addEventListener( "impress:stepenter", assertStepEnterWrapper ); + + // Things to check on impress:stepleave event -----------------------------// + var assertStepLeave = function( event ) { + assert.equal( event.target, sequence[ i ].left, + event.target.id + " triggered impress:stepleave event." ); + readyForNext(); + }; + + var assertStepLeaveWrapper = function( event ) { + window.setTimeout( function() { assertStepLeave( event ); }, wait ); + }; + root.addEventListener( "impress:stepleave", assertStepLeaveWrapper ); + + assert.ok( iframeWin.impress().next(), "next() called and returns ok (1->2)" ); + } ); // InitPresentation() + } ); // LoadIframe() +} ); + +QUnit.test( "Navigation Plugin - No-op tests", function( assert ) { + window.console.log( "Begin navigation no-op" ); + var done = assert.async(); + + loadIframe( "test/core_tests_presentation.html", assert, function() { + initPresentation( assert, function() { + var iframe = document.getElementById( "presentation-iframe" ); + var iframeDoc = iframe.contentDocument; + var iframeWin = iframe.contentWindow; + + var wait = 5; // Milliseconds + + var root = iframeDoc.querySelector( "div#impress" ); + + // This should never happen -----------------------------// + var assertStepEnter = function( event ) { + assert.ok( false, + event.target.id + " triggered impress:stepenter event." ); + }; + + var assertStepEnterWrapper = function( event ) { + window.setTimeout( function() { assertStepEnter( event ); }, wait ); + }; + root.addEventListener( "impress:stepenter", assertStepEnterWrapper ); + + // This should never happen -----------------------------// + var assertStepLeave = function( event ) { + assert.ok( false, + event.target.id + " triggered impress:stepleave event." ); + }; + + var assertStepLeaveWrapper = function( event ) { + window.setTimeout( function() { assertStepLeave( event ); }, wait ); + }; + root.addEventListener( "impress:stepleave", assertStepLeaveWrapper ); + + // These are no-op actions, we're already in step-1 -----------------------// + assert.ok( iframeWin.syn.click( "step-1", {} ), + "Click on step that is currently active, should do nothing." ); + assert.ok( iframeWin.syn.click( "linktofirst", {} ), + "Click on link pointing to step that is currently active, should do nothing." ); + + // After delay, if no event triggers are called. We're done. + window.setTimeout( function() { + window.console.log( "End navigation no-op" ); done(); + }, 3000 ); + } ); // InitPresentation() + } ); // LoadIframe() +} );