diff --git a/.travis.yml b/.travis.yml index 628db0b5..d1ba4696 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - 10 + - 13 addons: chrome: stable @@ -34,6 +34,7 @@ jobs: script: - npm run lint:hbs - npm run lint:js + - npm test:node - npm test # we recommend new addons test the current and previous LTS diff --git a/addon-test-support/-private/deprecate.js b/addon-test-support/-private/deprecate.js index 80a635c4..5ab1ef87 100644 --- a/addon-test-support/-private/deprecate.js +++ b/addon-test-support/-private/deprecate.js @@ -1,19 +1,20 @@ -import { deprecate } from '@ember/application/deprecations'; +// import { deprecate } from '@ember/application/deprecations'; const NAMESPACE = 'ember-cli-page-object'; // @todo: remove `_` after getting rid of the ember deprecate // for some reason renaming of the import leads to runrime issues export default function _deprecate(name, message, since, until) { - const [major, minor] = since.split('.'); + console.warn(NAMESPACE, name, message, since, until); + // const [major, minor] = since.split('.'); - deprecate(message, false, { - id: `${NAMESPACE}.${name}`, - for: NAMESPACE, - since: { - enabled: since - }, - until, - url: `https://ember-cli-page-object.js.org/docs/v${major}.${minor}.x/deprecations#${name}`, - }); + // deprecate(message, false, { + // id: `${NAMESPACE}.${name}`, + // for: NAMESPACE, + // since: { + // enabled: since + // }, + // until, + // url: `https://ember-cli-page-object.js.org/docs/v${major}.${minor}.x/deprecations#${name}`, + // }); } diff --git a/addon-test-support/-private/finders.js b/addon-test-support/-private/finders.js index f3249a31..a23d750c 100644 --- a/addon-test-support/-private/finders.js +++ b/addon-test-support/-private/finders.js @@ -1,11 +1,11 @@ import { - $, buildSelector, findClosestValue, guardMultiple } from './helpers'; import { getAdapter } from '../adapters'; import { throwBetterError, ELEMENT_NOT_FOUND } from './better-errors'; +import jQuery from 'jquery'; function getContainer(pageObjectNode, options) { return options.testContainer @@ -13,6 +13,26 @@ function getContainer(pageObjectNode, options) { || getAdapter().testContainer; } +function isWindow(window) { + return window && window.document; +} + +export function $(selector, container) { + const { window: adapterWindow } = getAdapter(); + + if (isWindow(adapterWindow)) { + if ((typeof window === 'undefined' || !isWindow(window))) { + // node.js environment + // see https://github.com/jquery/jquery/issues/3426#issuecomment-264649345 + return jQuery(adapterWindow)(selector, container) + } else { + throw new Error('adapter `window` within browser environment is not supported'); + } + } else { + return jQuery(selector, container); + } +} + /** * Finds a single element, otherwise fails * diff --git a/addon-test-support/-private/helpers.js b/addon-test-support/-private/helpers.js index 40d735e4..672dc6e4 100644 --- a/addon-test-support/-private/helpers.js +++ b/addon-test-support/-private/helpers.js @@ -1,7 +1,6 @@ -import { get } from '@ember/object'; import Ceibo from 'ceibo'; import deprecate from './deprecate'; -export { default as $ } from 'jquery'; +export { $ } from './finders'; function isPresent(value) { return typeof value !== 'undefined'; @@ -216,62 +215,6 @@ export function findClosestValue(node, property) { } } -/** - * @public - * - * Returns a boolean indicating whether an object contains a given property. - * The path to a nested property should be indicated by a dot-separated string. - * - * @param {Object} object - object to check for the target property - * @param {string} pathToProp - dot-separated path to property - * @return {Boolean} - */ -export function objectHasProperty(object, pathToProp) { - const pathSegments = pathToProp.split('.'); - - for (let i = 0; i < pathSegments.length; i++) { - const key = pathSegments[i]; - if (object === null || object === undefined || !object.hasOwnProperty(key)) { - return false; - } else { - object = object[key]; - } - } - - return true; -} - -/** - * @public - * - * Returns the value of an object property. If the property is a function, - * the return value is that function bound to its "owner." - * - * @param {Object} object - object on which to look up the target property - * @param {string} pathToProp - dot-separated path to property - * @return {Boolean|String|Number|Function|Null|Undefined} - value of property - */ -export function getProperty(object, pathToProp) { - const pathSegments = pathToProp.split('.'); - - if (pathSegments.length === 1) { - const value = get(object, pathToProp); - return typeof value === 'function' ? value.bind(object) : value; - } - - const pathToPropOwner = pathSegments.slice(0, -1).join('.'); - const propOwner = get(object, pathToPropOwner); - - if (propOwner === null || propOwner === undefined) { - return undefined; - } - - const propKey = pathSegments[pathSegments.length - 1]; - const value = get(propOwner, propKey); - - return typeof value === 'function' ? value.bind(propOwner) : value; -} - export function isPageObject(property) { if (property && typeof property === 'object') { let meta = Ceibo.meta(property); diff --git a/addon-test-support/macros/alias.js b/addon-test-support/macros/alias.js index 8d058279..2ba752b8 100644 --- a/addon-test-support/macros/alias.js +++ b/addon-test-support/macros/alias.js @@ -1,8 +1,5 @@ +import { get } from '@ember/object'; import { throwBetterError } from '../-private/better-errors'; -import { - getProperty, - objectHasProperty -} from '../-private/helpers'; import { chainable } from "../-private/chainable"; const ALIASED_PROP_NOT_FOUND = 'PageObject does not contain aliased property'; @@ -79,7 +76,7 @@ const ALIASED_PROP_NOT_FOUND = 'PageObject does not contain aliased property'; */ export function alias(pathToProp, options = {}) { return { - isDescriptor: true, + isDescriptor: true, get(key) { if (!objectHasProperty(this, pathToProp)) { @@ -103,3 +100,59 @@ export function alias(pathToProp, options = {}) { } }; } + +/** + * @private + * + * Returns a boolean indicating whether an object contains a given property. + * The path to a nested property should be indicated by a dot-separated string. + * + * @param {Object} object - object to check for the target property + * @param {string} pathToProp - dot-separated path to property + * @return {Boolean} + */ +function objectHasProperty(object, pathToProp) { + const pathSegments = pathToProp.split('.'); + + for (let i = 0; i < pathSegments.length; i++) { + const key = pathSegments[i]; + if (object === null || object === undefined || !object.hasOwnProperty(key)) { + return false; + } else { + object = object[key]; + } + } + + return true; +} + +/** + * @private + * + * Returns the value of an object property. If the property is a function, + * the return value is that function bound to its "owner." + * + * @param {Object} object - object on which to look up the target property + * @param {string} pathToProp - dot-separated path to property + * @return {Boolean|String|Number|Function|Null|Undefined} - value of property + */ +function getProperty(object, pathToProp) { + const pathSegments = pathToProp.split('.'); + + if (pathSegments.length === 1) { + const value = get(object, pathToProp); + return typeof value === 'function' ? value.bind(object) : value; + } + + const pathToPropOwner = pathSegments.slice(0, -1).join('.'); + const propOwner = get(object, pathToPropOwner); + + if (propOwner === null || propOwner === undefined) { + return undefined; + } + + const propKey = pathSegments[pathSegments.length - 1]; + const value = get(propOwner, propKey); + + return typeof value === 'function' ? value.bind(propOwner) : value; +} diff --git a/config/ember-try.js b/config/ember-try.js index f8da378f..ddb4f1ee 100644 --- a/config/ember-try.js +++ b/config/ember-try.js @@ -162,7 +162,7 @@ module.exports = function() { }, { name: 'node-tests', - command: 'npm run nodetest', + command: 'npm run test:ember-node', bower: { dependencies: {} } diff --git a/node-tests/basic-test.js b/node-tests/basic-test.js new file mode 100644 index 00000000..18672e7a --- /dev/null +++ b/node-tests/basic-test.js @@ -0,0 +1,53 @@ +// requires >= node@13 +// should be invoked like `node --es-module-specifier-resolution=node ./node-tests/run-test.js` + +import { create, visitable } from 'ember-cli-page-object'; +import Adapter from 'ember-cli-page-object/adapter'; +import { setAdapter } from 'ember-cli-page-object/adapters'; +import jsdom from 'jsdom'; +import QUnit from 'qunit'; +const { module, test } = QUnit; + +const RESPONSE_FIXTURE = ` +
+ + +`; + +class NodeJSAdapter extends Adapter { + visit() { + return new Promise((resolve) => { + setTimeout(() => { + const { JSDOM } = jsdom; + const { window } = (new JSDOM(RESPONSE_FIXTURE)); + + this.window = window; + + resolve(); + }, 1000) + }) + } + + get testContainer() { + return this.window.document; + } +} + +module('node.js', function(hooks) { + hooks.beforeEach(function() { + setAdapter(new NodeJSAdapter()); + }); + + test('it works!', async function(assert) { + const a = create({ + scope: '.test', + + visit: visitable(''), + }); + + await a.visit(); + + assert.strictEqual(a.isVisible, false); + assert.strictEqual(a.isPresent, true); + }); +}); diff --git a/package.json b/package.json index b9357e95..6d67be57 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "pageobject", "testing" ], + "type": "module", "repository": { "type": "git", "url": "https://github.com/san650/ember-cli-page-object.git" @@ -32,8 +33,9 @@ "start": "ember serve", "test": "ember test", "test:all": "ember try:each", + "test:ember-node": "mocha node-tests --recursive", + "test:node": "node --es-module-specifier-resolution=node ./node_modules/.bin/qunit node-tests/basic-test.js", "docs": "node ./docs", - "nodetest": "mocha node-tests --recursive", "prepublishOnly": "ember ts:precompile", "postpublish": "ember ts:clean" }, @@ -42,7 +44,7 @@ "@babel/preset-env": "^7.12.11", "broccoli-file-creator": "^2.1.1", "broccoli-merge-trees": "^2.0.0", - "ceibo": "~2.0.0", + "ceibo": "~2.1.0", "ember-cli-babel": "^7.24.0-beta.2", "ember-cli-node-assets": "^0.2.2", "ember-native-dom-helpers": "^0.6.3", @@ -95,6 +97,7 @@ "mkdirp": "^0.5.1", "mocha": "^4.0.1", "ncp": "^2.0.0", + "qunit": "^2.14.0", "qunit-dom": "^0.8.0", "rimraf": "^2.5.2", "typescript": "^3.7.5", @@ -103,6 +106,14 @@ "engines": { "node": "6.* || >= 7.*" }, + "exports": { + ".": "./addon-test-support", + "./adapter": "./addon-test-support/adapter", + "./adapters": "./addon-test-support/adapters", + "./adapters/*": "./addon-test-support/adapters/*.js", + "./extend": "./addon-test-support/extend", + "./macros": "./addon-test-support/macros" + }, "homepage": "http://ember-cli-page-object.js.org", "bugs": "https://github.com/san650/ember-cli-page-object/issues", "ember-addon": { diff --git a/tests/unit/-private/helpers-test.js b/tests/unit/-private/helpers-test.js index cb28a652..720e6413 100644 --- a/tests/unit/-private/helpers-test.js +++ b/tests/unit/-private/helpers-test.js @@ -1,10 +1,6 @@ import { test, module } from 'qunit'; import { create, collection } from 'ember-cli-page-object'; -import { - fullScope, - getProperty, - objectHasProperty -} from 'ember-cli-page-object/test-support/-private/helpers'; +import { fullScope, } from 'ember-cli-page-object/test-support/-private/helpers'; module('Unit | helpers | fullScope'); @@ -23,134 +19,3 @@ test('calculates full scope for components', function(assert) { assert.equal(fullScope(page.keyboard), '.calculator .keyboard'); assert.equal(fullScope(page.keyboard.numbers[0]), '.calculator .keyboard .numbers button:eq(0)'); }); - -module('Unit | helpers | objectHasProperty'); - -test('returns true when the object contains the property', function(assert) { - const object = { - foo: 100, - bar: undefined, - baz: { - fizz: null, - buzz: { - hello: 'world', - waldo() {} - } - }, - qux() {} - }; - - assert.equal(objectHasProperty(object, 'foo'), true, 'foo'); - assert.equal(objectHasProperty(object, 'bar'), true, 'bar'); - assert.equal(objectHasProperty(object, 'baz'), true, 'baz'); - assert.equal(objectHasProperty(object, 'qux'), true, 'qux'); - assert.equal(objectHasProperty(object, 'baz.fizz'), true, 'baz.fizz'); - assert.equal(objectHasProperty(object, 'baz.buzz'), true, 'baz.buzz'); - assert.equal(objectHasProperty(object, 'baz.buzz.hello'), true, 'baz.buzz.hello'); - assert.equal(objectHasProperty(object, 'baz.buzz.waldo'), true, 'baz.buzz.waldo'); -}); - -test('returns false when the object does not contain the property', function(assert) { - const object = { - foo: 100, - bar: undefined, - baz: { - fizz: null, - buzz: { - hello: 'world', - waldo() {} - } - }, - qux() {} - }; - - assert.equal(objectHasProperty(object, 'banana'), false, 'banana'); - assert.equal(objectHasProperty(object, 'banana.apple'), false, 'banana.apple'); - assert.equal(objectHasProperty(object, 'foo.banana'), false, 'foo.banana'); - assert.equal(objectHasProperty(object, 'bar.banana'), false, 'bar.banana'); - assert.equal(objectHasProperty(object, 'baz.banana'), false, 'baz.banana'); - assert.equal(objectHasProperty(object, 'qux.banana'), false, 'qux.banana'); - assert.equal(objectHasProperty(object, 'baz.banana.apple'), false, 'baz.banana.apple'); - assert.equal(objectHasProperty(object, 'baz.fizz.banana'), false, 'baz.fizz.banana'); - assert.equal(objectHasProperty(object, 'baz.buzz.banana'), false, 'baz.buzz.banana'); - assert.equal(objectHasProperty(object, 'baz.buzz.hello.banana'), false, 'baz.buzz.hello.banana'); - assert.equal(objectHasProperty(object, 'baz.buzz.waldo.banana'), false, 'baz.buzz.waldo.banana'); -}); - -module('Unit | helpers | getProperty'); - -test('returns top-level property', function(assert) { - const object = { - foo: 100, - bar: undefined, - baz: {} - }; - - assert.equal(getProperty(object, 'foo'), 100, 'foo'); - assert.equal(getProperty(object, 'bar'), undefined, 'bar'); - assert.deepEqual(getProperty(object, 'baz'), {}, 'baz'); -}); - -test('returns top-level bound method', function(assert) { - const object = { - qux() { return Object.keys(this); } - }; - - const result = getProperty(object, 'qux')(); - assert.deepEqual(result, ['qux']); -}); - -test('returns nested property', function(assert) { - const object = { - baz: { - fizz: null, - buzz: { - hello: 'world' - } - } - }; - - assert.equal(getProperty(object, 'baz.fizz'), null, 'baz.fizz'); - assert.deepEqual(getProperty(object, 'baz.buzz'), { hello: 'world' }, 'baz.buzz'); - assert.equal(getProperty(object, 'baz.buzz.hello'), 'world', 'baz.buzz.hello'); -}); - -test('returns nested bound method', function(assert) { - const object = { - baz: { - buzz: { - waldo() { return Object.keys(this); } - } - } - }; - - const result = getProperty(object, 'baz.buzz.waldo')(); - assert.deepEqual(result, ['waldo']); -}); - -test('returns undefined when the object does not contain the property', function(assert) { - const object = { - foo: 100, - bar: undefined, - baz: { - fizz: null, - buzz: { - hello: 'world', - waldo() {} - } - }, - qux() {} - }; - - assert.equal(getProperty(object, 'banana'), undefined, 'banana'); - assert.equal(getProperty(object, 'banana.apple'), undefined, 'banana.apple'); - assert.equal(getProperty(object, 'foo.banana'), undefined, 'foo.banana'); - assert.equal(getProperty(object, 'bar.banana'), undefined, 'bar.banana'); - assert.equal(getProperty(object, 'baz.banana'), undefined, 'baz.banana'); - assert.equal(getProperty(object, 'qux.banana'), undefined, 'qux.banana'); - assert.equal(getProperty(object, 'baz.banana.apple'), undefined, 'baz.banana.apple'); - assert.equal(getProperty(object, 'baz.fizz.banana'), undefined, 'baz.fizz.banana'); - assert.equal(getProperty(object, 'baz.buzz.banana'), undefined, 'baz.buzz.banana'); - assert.equal(getProperty(object, 'baz.buzz.hello.banana'), undefined, 'baz.buzz.hello.banana'); - assert.equal(getProperty(object, 'baz.buzz.waldo.banana'), undefined, 'baz.buzz.waldo.banana'); -});