diff --git a/browser/actions.js b/browser/actions.js index 02da74a5..19011c10 100644 --- a/browser/actions.js +++ b/browser/actions.js @@ -76,9 +76,9 @@ var clicks = [ } var selector = this.selector; FuncUnit.add({ - method: function(success, error){ + method: async function(success, error){ options = options || {}; - syn("_" + name, this.bind[0], options, success); + await syn[name](this.bind[0], options); }, success: success, error: "Could not " + name + " '" + this.selector+"'", @@ -146,9 +146,8 @@ $.extend(FuncUnit.prototype, { text = "[ctrl]a[ctrl-up]\b" } FuncUnit.add({ - method : function(success, error){ - syn("_type", this.bind[0], text, success); - + method : async function(success, error){ + await syn.type(this.bind[0], text); }, success : success, error : "Could not type " + text + " into " + this.selector, @@ -177,9 +176,8 @@ $.extend(FuncUnit.prototype, { keys = "[ctrl]a[ctrl-up]\b" } FuncUnit.add({ - method : function(success, error){ - syn("_type", this.bind[0], keys, success); - + method : async function(success, error){ + await syn.type(this.bind[0], keys); }, success : success, error : "Could not send the keys " + keys + " into " + this.selector, @@ -266,8 +264,8 @@ $.extend(FuncUnit.prototype, { var selector = this.selector; FuncUnit.add({ - method: function(success, error){ - syn("_drag", this.bind[0], options, success); + method: async function(success, error){ + await syn.drag(this.bind[0], options); }, success: success, error: "Could not drag " + this.selector, @@ -334,8 +332,8 @@ $.extend(FuncUnit.prototype, { var selector = this.selector; FuncUnit.add({ - method: function(success, error){ - syn("_move", this.bind[0], options, success); + method: async function(success, error){ + await syn.move(this.bind[0], options); }, success: success, error: "Could not move " + this.selector, diff --git a/new-src/actions.js b/new-src/actions.js new file mode 100644 index 00000000..cf0b7d68 --- /dev/null +++ b/new-src/actions.js @@ -0,0 +1,81 @@ +const syn = require("syn"); + +const funcUnitActions = { + click(){ + return simpleAsyncAction(this, syn.click); + }, + + rightClick() { + return simpleAsyncAction(this, syn.rightClick); + }, + + dblClick() { + return simpleAsyncAction(this, syn.dblclick); + }, + + scroll(direction, amount) { + let func; + + if (direction === 'left' || direction === 'right') { + func = function (el) { + el.scrollLeft = amount; + return el; + } + } + + if (direction === 'top' || direction === 'bottom') { + func = function (el) { + el.scrollTop = amount; + return el; + } + } + + return simpleSyncAction(this, func); + }, + + drag() { + return simpleAsyncActionWithArgs(this, syn.drag, arguments); + }, + + move() { + return simpleAsyncActionWithArgs(this, syn.move, arguments); + }, + + type(text) { + this.click(); + return simpleAsyncActionWithArgs(this, syn.type, arguments); + }, + + sendKeys(text) { + return simpleAsyncActionWithArgs(this, syn.type, arguments); + } +}; + + +// Helper functions + +function simpleAsyncActionWithArgs(elementPromise, action, args) { + return new elementPromise.constructor(function simpleActionWithArgs(resolve, reject) { + elementPromise.then(function (element) { + action(element, ...args).then(() => resolve(element), reject); + }, reject); + }); +} + +function simpleAsyncAction(elementPromise, action) { + return new elementPromise.constructor(function simpleAction(resolve, reject) { + elementPromise.then(function (element){ + action(element).then(()=> resolve(element), reject); + }, reject); + }); +} + +function simpleSyncAction(elementPromise, action) { + return new elementPromise.constructor(function simpleAction(resolve, reject) { + elementPromise.then(function (element){ + resolve( action(element) ); + }, reject); + }); +} + +module.exports = funcUnitActions; diff --git a/new-src/core.js b/new-src/core.js new file mode 100644 index 00000000..e66864d5 --- /dev/null +++ b/new-src/core.js @@ -0,0 +1,151 @@ +const Sizzle = require("sizzle"); + +const { + resolveWhenTrue, + makeExecutorToResolveWhenTrue +} = require('./helpers'); + +const funcUnitActions = require('./actions'); +const funcUnitGetters = require('./getters'); +const funcUnitTraversers = require('./traversers'); + +const funcUnitPrototypeMethods = { + ...funcUnitActions, + ...funcUnitGetters, + ...funcUnitTraversers +}; + +function makeFuncUnit(defaultWindow) { + + function FuncUnit(selector, context) { + let executor; + if(typeof selector === "string") { + let win = getWindow(selector, context || FuncUnit.defaultWindow); + + executor = makeExecutorToResolveWhenTrue({ + tester: function(){ + var documentElement = win.document.documentElement; + + if(!documentElement) { + return undefined; + } + var results = Sizzle(selector, documentElement); + // contains will return multiple elements nested inside each other, this helps take the right + // one. + + return takeFirstDeepestChild(results); + }, + rejectReason: () => new Error("Unable to find "+selector) + }) + } else if(typeof selector === "function") { + executor = selector; + } else { + throw "arguments not understood"; + } + return Reflect.construct(Promise, [executor], FuncUnit); + }; + + FuncUnit.defaultWindow = defaultWindow; + + Object.assign( + FuncUnit, + Object.fromEntries( + Reflect.ownKeys(Promise) + .filter(key => key !== "length" && key !== "name") + .map(key => [key, Promise[key]]) + ) + ); + // Create the prototype, add methods to it + FuncUnit.prototype = Object.create(Promise.prototype); + FuncUnit.prototype.constructor = FuncUnit; + + Object.assign(FuncUnit.prototype, funcUnitPrototypeMethods); + + Object.assign(FuncUnit, funcUnitStaticMethods); + return FuncUnit; +} + +const funcUnitStaticMethods = { + async useWindow(url){ + var width = window.innerWidth; + this.defaultWindow = window.open(url, "funcunit", "height=1000,toolbar=yes,status=yes,left="+width/4); + + if(this.defaultWindow && this.defaultWindow.___FUNCUNIT_OPENED) { + this.defaultWindow.close(); + this.defaultWindow = window.open(url, "funcunit", "height=1000,toolbar=yes,status=yes,left="+width/4); + } + let stealDone = false, ready = false; + + setTimeout(() => ready = true, 123); + + await resolveWhenTrue({ + tester: () => { + + + // make sure there is a document + if(!this.defaultWindow.document.documentElement) { + return; + } + if( this.defaultWindow.document.readyState !== "complete" || + this.defaultWindow.location.href === "about:blank" || + !this.defaultWindow.document.body ) { + return; + } + this.defaultWindow.___FUNCUNIT_OPENED = true; + + // wait for steal to fully load + if( this.defaultWindow.steal ) { + this.defaultWindow.steal.done().then(() => { + stealDone = true; + }); + } else { + stealDone = true; + } + return stealDone && ready; + } + }) + return this; + } +} + +function takeFirstDeepestChild(elements){ + var cur = elements[0]; + var index = 1; + var next; + while(next = elements[index]) { + if(!next) { + return cur; + } + if(! cur.contains(next) ) { + return cur; + } else { + cur = next; + index++; + } + } + return cur; +} + +function getWindowFromElement(element){ + const doc = element.ownerDocument; + return doc.defaultView || doc.parentWindow; +} + +function getWindow(element, context) { + + if (element.nodeName) { + return getWindowFromElement(element); + } else { + if (!context) { + return window; + } else if (context.nodeName === "#document") { + return getWindowFromElement(element); + } else if (context.self === context) { + return context; + } else { + throw new Error("can't find window") + } + } +} + +module.exports = makeFuncUnit(); diff --git a/new-src/getters.js b/new-src/getters.js new file mode 100644 index 00000000..4cdb158e --- /dev/null +++ b/new-src/getters.js @@ -0,0 +1,241 @@ +const { + makeExecutorToResolveWhenTrueAfterPromiseResolves +} = require('./helpers'); + +const funcUnitGetters = { + hasClass(classToken, compareHasClass = true) { + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => (targetElement.classList.contains(classToken) === compareHasClass) && targetElement, + rejectReason: () => new Error(`Element never had ${classToken} in classList`) + }); + return new this.constructor(executor); + }, + + visible() { + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => !!( targetElement.offsetHeight || targetElement.offsetWidth || targetElement.getClientRects().length ), + rejectReason: () => new Error('Element was never visible') + }); + return new this.constructor(executor); + }, + + val(newVal) { + if (arguments.length) { + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => targetElement.value === newVal, + rejectReason: () => new Error(`Element never had value: ${newVal}`) + }); + return new this.constructor(executor); + } else { + return simpleAsyncGetter(this, el => el.value); + } + }, + + text(waitForText) { + if (arguments.length) { + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => targetElement.textContent === waitForText, + rejectReason: () => new Error(`Element never had text: ${waitForText}`) + }); + return new this.constructor(executor); + } else { + return simpleAsyncGetter(this, el => el.textContent); + } + }, + + height(expectedHeight) { + if (arguments.length) { + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => calculateHeight(targetElement) === expectedHeight, + rejectReason: () => new Error(`Element never had a height of ${expectedHeight}`) + }); + return new this.constructor(executor); + } else { + return simpleAsyncGetter(this, calculateHeight); + } + }, + + width(expectedWidth) { + if (arguments.length) { + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => calculateWidth(targetElement) === expectedWidth, + rejectReason: () => new Error(`Element never had a width of ${expectedWidth}`) + }); + return new this.constructor(executor); + } else { + return simpleAsyncGetter(this, calculateWidth); + } + }, + + innerHeight(expectedHeight) { + if (arguments.length) { + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => calculateInnerHeight(targetElement) === expectedHeight, + rejectReason: () => new Error(`Element never had an innerHeight of ${expectedHeight}`) + }); + return new this.constructor(executor); + } else { + return simpleAsyncGetter(this, calculateInnerHeight); + } + }, + + innerWidth(expectedWidth) { + if (arguments.length) { + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => calculateInnerWidth(targetElement) === expectedWidth, + rejectReason: () => new Error(`Element never had an innerWidth of ${expectedWidth}`) + }); + return new this.constructor(executor); + } else { + return simpleAsyncGetter(this, calculateInnerWidth); + } + }, + + outerHeight() { + if (!arguments.length) { + // return outerHeight with no margin + return simpleAsyncGetter(this, calculateOuterHeight); + + } else if (arguments.length === 1 && typeof arguments[0] === 'boolean') { + // return outerHeight with margin + return simpleAsyncGetter(this, el => calculateOuterHeight(el, true)); + + } else if (arguments.length === 1 && typeof arguments[0] === 'number') { + // return outerHeight comparison with no margin + const expectedHeight = arguments[0]; + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => calculateOuterHeight(targetElement) === expectedHeight, + rejectReason: () => new Error(`Element never had an outerHeight of ${expectedHeight}`) + }); + return new this.constructor(executor); + + } else { + // return outerHeight comparison with margin (2 arguments) + const expectedHeight = arguments[0]; + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => calculateOuterHeight(targetElement, true) === expectedHeight, + rejectReason: () => new Error(`Element never had an outerHeight (margin=true) of ${expectedHeight}`) + }); + return new this.constructor(executor); + } + }, + + outerWidth() { + if (!arguments.length) { + // return outerWidth with no margin + return simpleAsyncGetter(this, calculateOuterWidth); + + } else if (arguments.length === 1 && typeof arguments[0] === 'boolean') { + // return outerWidth with margin + return simpleAsyncGetter(this, el => calculateOuterWidth(el, true)); + + } else if (arguments.length === 1 && typeof arguments[0] === 'number') { + // return outerWidth comparison with no margin + const expectedWidth = arguments[0]; + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => calculateOuterWidth(targetElement) === expectedWidth, + rejectReason: () => new Error(`Element never had an outerWidth of ${expectedWidth}`) + }); + return new this.constructor(executor); + + } else { + // return outerWidth comparison with margin (2 arguments) + const expectedWidth = arguments[0]; + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => calculateOuterWidth(targetElement, true) === expectedWidth, + rejectReason: () => new Error(`Element never had an outerWidth (margin=true) of ${expectedWidth}`) + }); + return new this.constructor(executor); + } + }, + + scrollTop(distance) { + if (arguments.length) { + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: function(targetElement) { + return targetElement && targetElement.scrollTop === distance; + }, + rejectReason: () => new Error(`Element never reached scrollTop of ${distance}`) + }); + return new this.constructor(executor); + + } else { + return simpleAsyncGetter(this, el => el.scrollTop); + } + }, + + scrollLeft(distance) { + if (arguments.length) { + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: function(targetElement) { + return targetElement && targetElement.scrollLeft === distance; + }, + rejectReason: () => new Error(`Element never reached scrollLeft of ${distance}`) + }); + return new this.constructor(executor); + + } else { + return simpleAsyncGetter(this, el => el.scrollLeft); + } + } +}; + + +// Helper functions + +function simpleAsyncGetter(elementPromise, getter) { + return new elementPromise.constructor(function simpleAction(resolve, reject){ + elementPromise.then(function(element){ + resolve( getter(element) ); + }, reject); + }); +} + +function getDimension(element, add, subtract = []) { + const styles = getComputedStyle(element); + + const sum = add.reduce((total, property) => { + return total + parseFloat(styles[property], 10) + }, 0); + + return subtract.reduce((total, property) => { + return total - parseFloat(styles[property]); + }, sum); +} + +function calculateHeight(element) { + return getDimension(element, ['height'], ['padding-top', 'padding-bottom']); +} + +function calculateInnerHeight(element) { + return getDimension(element, ['height']); +} + +function calculateOuterHeight (element, includeMargin = false) { + const addProperties = ['height', 'border-top', 'border-bottom']; + if (includeMargin) { + addProperties.push('margin-top'); + addProperties.push('margin-bottom'); + } + return getDimension(element, addProperties); +} + +function calculateWidth(element) { + return getDimension(element, ['width'], ['padding-left', 'padding-right']); +} + +function calculateInnerWidth(element) { + return getDimension(element, ['width']); +} + +function calculateOuterWidth(element, includeMargin = false) { + const addProperties = ['width', 'border-left', 'border-right']; + if (includeMargin) { + addProperties.push('margin-left'); + addProperties.push('margin-right'); + } + return getDimension(element, addProperties); +} + + +module.exports = funcUnitGetters; diff --git a/new-src/helpers.js b/new-src/helpers.js new file mode 100644 index 00000000..e7261f49 --- /dev/null +++ b/new-src/helpers.js @@ -0,0 +1,54 @@ +function resolveWhenTrue(options) { + return new Promise(makeExecutorToResolveWhenTrue(options)); +} + +function makeExecutorToResolveWhenTrue(options) { + return function(resolve, reject) { + checkTester(resolve, reject, options); + } +} + +function makeExecutorToResolveWhenTrueAfterPromiseResolves(promise, options) { + return function(resolve, reject) { + promise.then(function(value) { + checkTester(resolve, reject, { ...options, testerArgument: value }); + }, reject) + } +} + +function makeExecutorThatPerformsActionAfterPromise(promise, action) { + return function(resolve, reject) { + promise.then(action, reject) + } +} + +function checkTester(resolve, reject, { + interval = 10, + timeout = 10000, + tester, + resolveValue = (x) => x, + rejectReason = () => new Error("Poller timeout"), + testerArgument +}) { + const start = new Date(); + function check(){ + let testerResult = tester(testerArgument) + if(testerResult) { + resolve(resolveValue(testerResult)); + return true + } else if( (new Date() - start) > timeout ) { + reject( rejectReason() ) + } else { + setTimeout(check, interval); + } + } + check(); +} + +module.exports = { + checkTester, + resolveWhenTrue, + makeExecutorToResolveWhenTrue, + makeExecutorThatPerformsActionAfterPromise, + makeExecutorToResolveWhenTrueAfterPromiseResolves +} diff --git a/new-src/traversers.js b/new-src/traversers.js new file mode 100644 index 00000000..5884a4ee --- /dev/null +++ b/new-src/traversers.js @@ -0,0 +1,24 @@ +const Sizzle = require("sizzle"); +const { + makeExecutorToResolveWhenTrueAfterPromiseResolves +} = require('./helpers'); + +const funcUnitTraversers = { + closest(selector){ + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => targetElement.closest(selector), + rejectReason: () => new Error(`Unable to find closest ${selector}`) + }); + return new this.constructor(executor); + }, + + find(selector){ + const executor = makeExecutorToResolveWhenTrueAfterPromiseResolves(this, { + tester: targetElement => Sizzle(selector, targetElement)[0], + rejectReason: () => new Error(`Unable to find ${selector}`) + }); + return new this.constructor(executor); + } +}; + +module.exports = funcUnitTraversers; diff --git a/new-tests/actions_test.js b/new-tests/actions_test.js new file mode 100644 index 00000000..5638fe55 --- /dev/null +++ b/new-tests/actions_test.js @@ -0,0 +1,110 @@ +const QUnit = require("steal-qunit"); +const F = require("funcunit/new-src/core"); + +QUnit.module('funcunit actions', { + beforeEach: async assert => { + const done = assert.async(); + await F.useWindow(`${__dirname}/test-setup/qunit2_tests.html`); + done(); + } +}); + + +// Click Events -------------------- + +QUnit.test('click', async assert => { + const done = assert.async(); + const element = await F('#foo').click(); + assert.ok(element.classList.contains('iWasClicked'), '#foo was clicked'); + done(); +}); + +QUnit.test('right click', async assert => { + const done = assert.async(); + const element = await F('#foo').rightClick(); + assert.ok(element.classList.contains('iWasRightClicked'), '#foo was right clicked'); + done(); +}); + +QUnit.test('double click', async assert => { + const done = assert.async(); + const element = await F('#foo').dblClick(); + assert.ok(element.classList.contains('iWasDoubleClicked'), '#foo was double clicked'); + done(); +}); + + +// Scroll -------------------- + +QUnit.test('scroll down', async assert => { + const done = assert.async(); + await F.useWindow(`${__dirname}/test-setup/scroll.html`); + const element = await F('#scrolldiv').scroll('top', 100); + assert.equal(100, element.scrollTop, 'scroll vertical worked'); + const scrollTopValue = await F('#scrolldiv').scrollTop(); + assert.equal(100, scrollTopValue, 'scrollTop getter worked'); + done(); +}); + +QUnit.test('scroll right', async assert => { + const done = assert.async(); + await F.useWindow(`${__dirname}/test-setup/scroll.html`); + const element = await F('#scrolldiv').scroll('right', 100); + assert.equal(100, element.scrollLeft, 'scroll horizontal worked'); + const scrollLeftValue = await F('#scrolldiv').scrollLeft(); + assert.equal(100, scrollLeftValue, 'scrollLeft getter worked'); + done(); +}); + + +// Drag & Move Events -------------------- + +QUnit.test('drag', async assert => { + const done = assert.async(); + + await F.useWindow(`${__dirname}/test-setup/drag.html`); + + await F('#drag').drag('#drop'); + await F('#clicker').click(); + const dragged = await F('.status').text('dragged', 'drag worked correctly'); + assert.ok(dragged); + + done(); +}); + +QUnit.test('move', async assert => { + const done = assert.async(); + + await F.useWindow(`${__dirname}/test-setup/drag.html`); + + await F('#start').move('#end'); + await F('#typer').type('javascriptmvc'); + const moved = await F('#typer').val('javascriptmvc', 'move worked correctly'); + assert.ok(moved); + + done(); +}); + + +// Keyboard Events -------------------- + +QUnit.test('type', async assert => { + const done = assert.async(); + const textToType = 'hello type'; + const element = await F('#typehere').type(textToType); + assert.equal(textToType, element.value, 'typed text matches'); + assert.ok(element.classList.contains('iWasClicked'), 'the element was clicked'); + done(); +}); + +QUnit.test('send keys', async assert => { + const done = assert.async(); + const keysToSend = 'hello sendKeys'; + const element = await F('#typehere').sendKeys(keysToSend); + assert.equal(keysToSend, element.value, 'sent keys matches'); + assert.ok(!element.classList.contains('iWasClicked'), 'the element was not clicked'); + done(); +}); + + + diff --git a/new-tests/dialogs_test.js b/new-tests/dialogs_test.js new file mode 100644 index 00000000..3f8a57f7 --- /dev/null +++ b/new-tests/dialogs_test.js @@ -0,0 +1,43 @@ +const QUnit = require("steal-qunit"); +const F = require("funcunit/new-src/core"); + +QUnit.module('funcunit dialogs', { + beforeEach: async assert => { + const done = assert.async(); + await F.useWindow(`${__dirname}/test-setup/confirm.html`); + done(); + } +}); + +// funcunit sets text for the next dialog +// F.confirm('confirm text'); + +QUnit.test('window alert', async assert => { + const done = assert.async(); + + await F('#alert').click(); + const alerted = await F('#alert').text('I was alerted', 'window.alert overridden'); + assert.ok(alerted, 'window alert dialog was overridden'); + + done(); +}); + +QUnit.test('window confirm', async assert => { + const done = assert.async(); + + await F('#confirm').click(); + const confirmed = await F('#confirm').text('I was confirmed', 'window.confirm overridden'); + assert.ok(confirmed, 'window confirm dialog was overridden'); + + done(); +}); + +QUnit.test('window prompt', async assert => { + const done = assert.async(); + + await F('#prompt').click(); + const prompted = await F('#prompt').text('I was prompted', 'window.prompt overridden'); + assert.ok(prompted, 'window prompt dialog was overridden'); + + done(); +}); diff --git a/new-tests/getters_test.js b/new-tests/getters_test.js new file mode 100644 index 00000000..4bd1fd08 --- /dev/null +++ b/new-tests/getters_test.js @@ -0,0 +1,142 @@ +const QUnit = require("steal-qunit"); +const F = require("funcunit/new-src/core"); + +QUnit.module("funcunit getters",{ + beforeEach: async function(assert) { + const done = assert.async(); + await F.useWindow(__dirname+"/test-setup/qunit2_tests.html"); + done(); + } +}); + + +// height, innerHeight, outerHeight/margin +QUnit.test('get and compare height', async assert => { + const done = assert.async(); + + const elementHeight = await F('#dimensions-div').height(); + assert.equal(8, elementHeight); + + const heightComparison = await F('#dimensions-div').height(elementHeight); + assert.ok(heightComparison); + + done(); +}); + +QUnit.test('get and compare innerHeight', async assert => { + const done = assert.async(); + + const elementInnerHeight = await F('#dimensions-div').innerHeight(); + assert.equal(10, elementInnerHeight); + + const heightComparison = await F('#dimensions-div').innerHeight(elementInnerHeight); + assert.ok(heightComparison); + + done(); +}); + +QUnit.test('get and compare outerHeight', async assert => { + const done = assert.async(); + + const elementOuterHeight = await F('#dimensions-div').outerHeight(); + assert.equal(12, elementOuterHeight); + + const heightComparison = await F('#dimensions-div').outerHeight(elementOuterHeight); + assert.ok(heightComparison); + + const elementOuterHeightMargin = await F('#dimensions-div').outerHeight(true); + assert.equal(14, elementOuterHeightMargin); + + const heightComparisonMargin = await F('#dimensions-div').outerHeight(elementOuterHeightMargin, true); + assert.ok(heightComparisonMargin); + + done(); +}); + + +// width, innerWidth, outerWidth(margin) +QUnit.test('get and compare width', async assert => { + const done = assert.async(); + + const elementwidth = await F('#dimensions-div').width(); + assert.equal(18, elementwidth); + + const widthComparison = await F('#dimensions-div').width(elementwidth); + assert.ok(widthComparison); + + done(); +}); + +QUnit.test('get and compare innerWidth', async assert => { + const done = assert.async(); + + const elementInnerWidth = await F('#dimensions-div').innerWidth(); + assert.equal(20, elementInnerWidth); + + const widthComparison = await F('#dimensions-div').innerWidth(elementInnerWidth); + assert.ok(widthComparison); + + done(); +}); + +QUnit.test('get and compare outerWidth', async assert => { + const done = assert.async(); + + const elementOuterWidth = await F('#dimensions-div').outerWidth(); + assert.equal(22, elementOuterWidth); + + const widthComparison = await F('#dimensions-div').outerWidth(elementOuterWidth); + assert.ok(widthComparison); + + const elementOuterWidthMargin = await F('#dimensions-div').outerWidth(true); + assert.equal(24, elementOuterWidthMargin); + + const widthComparisonMargin = await F('#dimensions-div').outerWidth(elementOuterWidthMargin, true); + assert.ok(widthComparisonMargin); + + done(); +}); + + +// Scroll +QUnit.test('get and compare scrollTop', async assert => { + const done = assert.async(); + + await F.useWindow(`${__dirname}/test-setup/scroll.html`); + await F('#innerdiv').click(); // autoscrolls 100px + + const elementScrollTop = await F('#scrolldiv').scrollTop(); + assert.equal(100, elementScrollTop); + + const scrollComparison = await F('#scrolldiv').scrollTop(100); + assert.ok(scrollComparison); + + done(); +}); + +QUnit.test('get and compare scrollLeft', async assert => { + const done = assert.async(); + + await F.useWindow(`${__dirname}/test-setup/scroll.html`); + await F('#innerdiv').click(); // autoscrolls 100px + + const elementScrollTop = await F('#scrolldiv').scrollLeft(); + assert.equal(100, elementScrollTop); + + const scrollComparison = await F('#scrolldiv').scrollLeft(100); + assert.ok(scrollComparison); + + done(); +}); + + +// visible +QUnit.test('element is visible', async assert => { + const done = assert.async(); + + const isVisible = await F('.baz').visible(); + assert.ok(isVisible); + + done(); +}); + diff --git a/new-tests/open_test.js b/new-tests/open_test.js new file mode 100644 index 00000000..faa0e630 --- /dev/null +++ b/new-tests/open_test.js @@ -0,0 +1,47 @@ +const QUnit = require("steal-qunit"); +const F = require("funcunit/new-src/core"); + +QUnit.module('funcunit open windows', { + beforeEach: async assert => { + const done = assert.async(); + // await F.useWindow(`${__dirname}/test-setup/qunit2_tests.html`); + done(); + } +}); + +// QUnit.test('F.open accepts a window', async assert => { +// const done = assert.async(); +// await F.useWindow(window); +// F('#tester').click(); +// F("#tester").text("Changed", "Changed link") + +// // F.open(frames["myapp"]); +// // F("#typehere").type("").type("javascriptmvc") +// // F("#seewhatyoutyped").text("typed javascriptmvc","typing"); + +// done(); +// }) + +QUnit.test("Back to back opens", async assert => { + const done = assert.async(); + + await F.useWindow(`${__dirname}/test-setup/myotherapp.html`); + await F.useWindow(`${__dirname}/test-setup/myapp.html`); + + await F("#changelink").click(); + const changed = await F("#changelink").text("Changed","href javascript run"); + + assert.ok(changed); + + done(); +}); + + +// QUnit.test('Testing win.confirm in multiple pages', function() { +// F.open('//test/open/first.html'); +// F('.next').click(); + +// F('.show-confirm').click(); +// F.confirm(true); +// F('.results').text('confirmed!', "Confirm worked!"); +// }) \ No newline at end of file diff --git a/new-tests/test-setup/confirm.html b/new-tests/test-setup/confirm.html new file mode 100644 index 00000000..09b6ad5b --- /dev/null +++ b/new-tests/test-setup/confirm.html @@ -0,0 +1,50 @@ + + + + + + Confirm! + Alert! + Prompt! + + diff --git a/new-tests/test-setup/drag.html b/new-tests/test-setup/drag.html new file mode 100644 index 00000000..6f5156a1 --- /dev/null +++ b/new-tests/test-setup/drag.html @@ -0,0 +1,45 @@ + + + + Synthetic Typing Test + + + +

start

+
Move over me
+

end

+
+
+
+
+
+ + + + diff --git a/new-tests/test-setup/drag.js b/new-tests/test-setup/drag.js new file mode 100644 index 00000000..b05b1452 --- /dev/null +++ b/new-tests/test-setup/drag.js @@ -0,0 +1,25 @@ +steal("jquerypp/index.js", function($){ + window.jQuery = $; + + var hoveredOnce = false; + $(".over").bind('mouseover',function(){ + if (!hoveredOnce) { + $(this).addClass('hover') + $(document.body).append("") + hoveredOnce = true; + console.log('hovered'); + } + }) + + $('#drag') + .on("draginit", function(){}) + + $('#drop') + .on("dropover", function(){ + $(document.body).append("click") + $("#clicker").click(function(){ + $(".status").html("dragged") + }) + }) + +}) diff --git a/new-tests/test-setup/myapp.html b/new-tests/test-setup/myapp.html new file mode 100644 index 00000000..d80efaca --- /dev/null +++ b/new-tests/test-setup/myapp.html @@ -0,0 +1,156 @@ + + + + Untitled Document + + + +

Hello World

+ + + + +

+

+

+ +
Change Thist Text
+ Change +
+
+

hasClass

+
Click Me
+

Exists

+
Click Me
+

Trigger Custom Event

+
Trigger Me
+
Confirm Me +
+
+
Right Click Me
+
+ + +
+ + + diff --git a/new-tests/test-setup/myotherapp.html b/new-tests/test-setup/myotherapp.html new file mode 100644 index 00000000..21835886 --- /dev/null +++ b/new-tests/test-setup/myotherapp.html @@ -0,0 +1,14 @@ + + + + Untitled Document + + + +

Goodbye World

+ + \ No newline at end of file diff --git a/new-tests/test-setup/qunit2_tests.html b/new-tests/test-setup/qunit2_tests.html new file mode 100644 index 00000000..4cc664c7 --- /dev/null +++ b/new-tests/test-setup/qunit2_tests.html @@ -0,0 +1,36 @@ + + + + Synthetic Typing Test + + + +
+
+ Holler! +
combo
+
+
combo
+
another
+
+
+ +
+
+ + + + + diff --git a/new-tests/test-setup/scroll.html b/new-tests/test-setup/scroll.html new file mode 100644 index 00000000..d2100e4f --- /dev/null +++ b/new-tests/test-setup/scroll.html @@ -0,0 +1,25 @@ + + + + + Untitled Document + + +

Scroll Test

+
+
+ +
+ + + + + diff --git a/new-tests/test.html b/new-tests/test.html new file mode 100644 index 00000000..c1009a51 --- /dev/null +++ b/new-tests/test.html @@ -0,0 +1,18 @@ + + + + + + + Document + + + + +
+ + + + + diff --git a/new-tests/test.js b/new-tests/test.js new file mode 100644 index 00000000..c1370564 --- /dev/null +++ b/new-tests/test.js @@ -0,0 +1,22 @@ +const QUnit = require("steal-qunit"); +const F = require("funcunit/new-src/core"); + +QUnit.module("funcunit - jQuery conflict") + +QUnit.test("basics", async function(assert) { + const button = document.createElement("button"); + button.innerHTML = "click"; + button.onclick = () => button.innerHTML = "clicked"; + + document.querySelector("#qunit-fixture").append(button); + + await F("#qunit-fixture button").click(); + + assert.equal(button.innerHTML, "clicked"); +}); + +require('./actions_test'); +require('./getters_test'); +require("./traversers_test"); +require('./dialogs_test'); +require('./open_test'); diff --git a/new-tests/traversers_test.js b/new-tests/traversers_test.js new file mode 100644 index 00000000..51d94340 --- /dev/null +++ b/new-tests/traversers_test.js @@ -0,0 +1,40 @@ +const QUnit = require("steal-qunit"); +const F = require("funcunit/new-src/core"); + +QUnit.module("funcunit traversers",{ + beforeEach: async function(assert) { + const done = assert.async(); + await F.useWindow(`${__dirname}/test-setup/qunit2_tests.html`); + done(); + } +}); + +QUnit.test("find closest", async function(assert){ + const done = assert.async(); + + let element = await F("a:contains('Holler')").closest("#foo").click(); + assert.ok(element.classList.contains("iWasClicked"),"we clicked #foo"); + + element = await F("a:contains('Holler')").closest(".baz").click(); + assert.ok(element.classList.contains("iWasClicked"),"we clicked .baz"); + + done(); +}); + +QUnit.test("find with traversers", async function(assert){ + const done = assert.async(); + + await F(":contains('Holler')") + .closest("#foo") + .find(".combo") + .hasClass("combo", true) + .click(); + + await F(".combo:eq(0)").hasClass("iWasClicked", true); + assert.ok(true, "first was clicked"); + + await F(".combo:eq(1)").hasClass("iWasClicked", false); + assert.ok(true, "first was not clicked"); + + done(); +}); diff --git a/package.json b/package.json index b163e8a9..713ac5f7 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ }, "main": "dist/cjs/funcunit.js", "dependencies": { - "syn": "^0.14.1" + "sizzle": "^2.3.6", + "syn": "^1.0.0-pre.3" }, "devDependencies": { "can-define": "^0.8.0", @@ -65,8 +66,7 @@ "steal-css": "^1.0.0", "steal-jasmine": "0.0.3", "steal-mocha": "0.0.3", - "steal-qunit": "^1.0.0", - "steal-qunit2": "^2.0.0", + "steal-qunit": "^2.0.0", "steal-tools": "^2.0.0", "testee": "^0.9.1" },