From 9c9e0e9180fe39578abc37dc1a1c69a414f94a0b Mon Sep 17 00:00:00 2001 From: mum-never-proud Date: Sun, 29 Mar 2020 14:30:15 +0530 Subject: [PATCH 1/5] add support for exact text match --- addon-test-support/-private/register-helpers.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 addon-test-support/-private/register-helpers.js diff --git a/addon-test-support/-private/register-helpers.js b/addon-test-support/-private/register-helpers.js new file mode 100644 index 00000000..87ca807b --- /dev/null +++ b/addon-test-support/-private/register-helpers.js @@ -0,0 +1,13 @@ +import $ from '-jquery'; + +function registerContainsExactHelper() { + $.expr[':'].containsExact = function(a, i, m) { + const textToFind = m[3].replace(/[-[\]{}(')*+?.[,\\^$|#\s]/g, '\\$&'); // escape special character for regex + + return $(a).text().match("^" + textToFind + "$"); + }; +} + +export default function() { + registerContainsExactHelper(); +} From 1b120509e2da12947104273de29050c0d95ed05f Mon Sep 17 00:00:00 2001 From: mum-never-proud Date: Sun, 29 Mar 2020 14:31:55 +0530 Subject: [PATCH 2/5] misc --- addon-test-support/-private/helpers.js | 4 ++- addon-test-support/create.js | 5 ++++ addon-test-support/extend/find-many.js | 2 +- addon-test-support/extend/find-one.js | 2 +- .../-private/properties/click-on-text-test.ts | 29 +++++++++++++++++++ types/index.d.ts | 1 + 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/addon-test-support/-private/helpers.js b/addon-test-support/-private/helpers.js index f521f1df..dbe05f91 100644 --- a/addon-test-support/-private/helpers.js +++ b/addon-test-support/-private/helpers.js @@ -56,7 +56,9 @@ class Selector { } if (this.targetFilters.contains) { - filters.push(`:contains("${this.targetFilters.contains}")`); + const exactFilter = this.targetFilters.exact ? 'Exact': ''; + + filters.push(`:contains${exactFilter}("${this.targetFilters.contains}")`); } if (typeof this.targetFilters.at === 'number') { diff --git a/addon-test-support/create.js b/addon-test-support/create.js index 045378b9..c80a1da3 100644 --- a/addon-test-support/create.js +++ b/addon-test-support/create.js @@ -2,9 +2,14 @@ import Ceibo from 'ceibo'; import { deprecate } from '@ember/application/deprecations'; import { render, setContext, removeContext } from './-private/context'; import { assign, getPageObjectDefinition, isPageObject, storePageObjectDefinition } from './-private/helpers'; +import registerHelpers from './-private/register-helpers'; import { visitable } from './properties/visitable'; import dsl from './-private/dsl'; +// register jQuery helpers initially + +registerHelpers(); + function assignDescriptors(target, source) { Object.getOwnPropertyNames(source).forEach((key) => { const descriptor = Object.getOwnPropertyDescriptor(source, key); diff --git a/addon-test-support/extend/find-many.js b/addon-test-support/extend/find-many.js index 876a9f35..863f3141 100644 --- a/addon-test-support/extend/find-many.js +++ b/addon-test-support/extend/find-many.js @@ -40,7 +40,7 @@ export function findMany(pageObjectNode, targetSelector, options = {}) { }); const filteredOptions = filterWhitelistedOption(options, [ - 'resetScope', 'visible', 'testContainer', 'contains', 'scope', 'at', 'last' + 'resetScope', 'visible', 'testContainer', 'contains', 'scope', 'at', 'last', 'exact' ]); const opts = Object.assign({}, filteredOptions, { multiple: true }); return getExecutionContext(pageObjectNode).find(targetSelector, opts).get(); diff --git a/addon-test-support/extend/find-one.js b/addon-test-support/extend/find-one.js index bb8caea3..ddba1db9 100644 --- a/addon-test-support/extend/find-one.js +++ b/addon-test-support/extend/find-one.js @@ -38,7 +38,7 @@ import { filterWhitelistedOption } from "../-private/helpers"; */ export function findOne(pageObjectNode, targetSelector, options = {}) { const filteredOptions = filterWhitelistedOption(options, [ - 'resetScope', 'visible', 'testContainer', 'contains', 'at', 'last', 'scope', 'pageObjectKey' + 'resetScope', 'visible', 'testContainer', 'contains', 'at', 'last', 'scope', 'pageObjectKey', 'exact' ]); const opts = Object.assign({}, filteredOptions, { multiple: false }); return getExecutionContext(pageObjectNode).findWithAssert(targetSelector, opts).get(0); diff --git a/tests/unit/-private/properties/click-on-text-test.ts b/tests/unit/-private/properties/click-on-text-test.ts index 7ca752d2..7f85c36f 100644 --- a/tests/unit/-private/properties/click-on-text-test.ts +++ b/tests/unit/-private/properties/click-on-text-test.ts @@ -233,4 +233,33 @@ moduleForProperty('clickOnText', function(test) { return page.foo('Click me'); }, /page\.foo/, 'Element not found'); }); + + test('should click on element matching exact text', async function(assert) { + assert.expect(2); + + let page = create({ + foo: clickOnText('button', { exact: true }) + }); + + await this.adapter.createTemplate(this, page, ` + + + + `); + + this.adapter.$('button:containsExact("foo")').one('click', function() { + assert.ok(true); + }); + + this.adapter.$('button:contains("foobar")').one('click', function() { + assert.ok(true); + }); + + await this.adapter.await(page.foo('foo')); + + + await this.adapter.throws(assert, function() { + return page.foo('fooobar'); + }, /page\.foo/, 'Element not found'); + }); }); diff --git a/types/index.d.ts b/types/index.d.ts index 4fa01c25..cc8e3b54 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -147,6 +147,7 @@ declare module 'ember-cli-page-object/-private' { scope?: string; last?: boolean; visible?: boolean; + exact?: boolean; at?: number; } From 32288ad11c1b3216a4c816cbbd313d3b9fdf42e4 Mon Sep 17 00:00:00 2001 From: mum-never-proud Date: Sun, 29 Mar 2020 14:39:13 +0530 Subject: [PATCH 3/5] oops revert --- addon-test-support/extend/find-many.js | 2 +- addon-test-support/extend/find-one.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addon-test-support/extend/find-many.js b/addon-test-support/extend/find-many.js index 863f3141..876a9f35 100644 --- a/addon-test-support/extend/find-many.js +++ b/addon-test-support/extend/find-many.js @@ -40,7 +40,7 @@ export function findMany(pageObjectNode, targetSelector, options = {}) { }); const filteredOptions = filterWhitelistedOption(options, [ - 'resetScope', 'visible', 'testContainer', 'contains', 'scope', 'at', 'last', 'exact' + 'resetScope', 'visible', 'testContainer', 'contains', 'scope', 'at', 'last' ]); const opts = Object.assign({}, filteredOptions, { multiple: true }); return getExecutionContext(pageObjectNode).find(targetSelector, opts).get(); diff --git a/addon-test-support/extend/find-one.js b/addon-test-support/extend/find-one.js index ddba1db9..bb8caea3 100644 --- a/addon-test-support/extend/find-one.js +++ b/addon-test-support/extend/find-one.js @@ -38,7 +38,7 @@ import { filterWhitelistedOption } from "../-private/helpers"; */ export function findOne(pageObjectNode, targetSelector, options = {}) { const filteredOptions = filterWhitelistedOption(options, [ - 'resetScope', 'visible', 'testContainer', 'contains', 'at', 'last', 'scope', 'pageObjectKey', 'exact' + 'resetScope', 'visible', 'testContainer', 'contains', 'at', 'last', 'scope', 'pageObjectKey' ]); const opts = Object.assign({}, filteredOptions, { multiple: false }); return getExecutionContext(pageObjectNode).findWithAssert(targetSelector, opts).get(0); From 7776cbe7a814c1ff759124b1f24ad55afc6a5b6a Mon Sep 17 00:00:00 2001 From: mum-never-proud Date: Wed, 1 Apr 2020 13:33:41 +0530 Subject: [PATCH 4/5] remove exact option leak --- types/index.d.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index cc8e3b54..dcc121c3 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -5,6 +5,7 @@ declare module 'ember-cli-page-object' { Definition, FindOptions, TriggerOptions, + ClickOnTextOptions, GetterDescriptor, MethodDescriptor, DSL @@ -31,7 +32,7 @@ declare module 'ember-cli-page-object' { // Actions function clickable(scope?: string, userOptions?: FindOptions): MethodDescriptor<(this: T) => T>; - function clickOnText(scope?: string, userOptions?: FindOptions): MethodDescriptor<(this: T, text: string) => T>; + function clickOnText(scope?: string, userOptions?: ClickOnTextOptions): MethodDescriptor<(this: T, text: string) => T>; function fillable(scope?: string, userOptions?: FindOptions): MethodDescriptor<(this: T, clueOrContent: string, content?: string) => T>; function selectable(scope?: string, userOptions?: FindOptions): MethodDescriptor<(this: T, clueOrContent: string, content?: string) => T>; function triggerable(event: string, scope?: string, eventOptions?: TriggerOptions, options?: FindOptions): MethodDescriptor<(this: T, options?: {}) => T>; @@ -147,7 +148,6 @@ declare module 'ember-cli-page-object/-private' { scope?: string; last?: boolean; visible?: boolean; - exact?: boolean; at?: number; } @@ -163,4 +163,8 @@ declare module 'ember-cli-page-object/-private' { resetScope?: boolean; testContainer?: string|HTMLElement|JQuery; } + + interface ClickOnTextOptions extends FindOptions { + exact?: boolean + } } From caf14ebe7ace130ddd6217e78479cb245a0f3f53 Mon Sep 17 00:00:00 2001 From: mum-never-proud Date: Thu, 2 Apr 2020 14:24:08 +0530 Subject: [PATCH 5/5] removed dependency of jQuery --- .../-private/execution_context/acceptance.js | 12 ++++++++-- .../-private/execution_context/integration.js | 14 ++++++++--- .../native-events-context.js | 12 ++++++++-- .../-private/execution_context/rfc268.js | 10 +++++++- addon-test-support/-private/helpers.js | 23 ++++++++++++++++--- .../-private/register-helpers.js | 13 ----------- addon-test-support/create.js | 5 ---- .../properties/click-on-text.js | 4 ++-- .../-private/properties/click-on-text-test.ts | 2 +- 9 files changed, 63 insertions(+), 32 deletions(-) delete mode 100644 addon-test-support/-private/register-helpers.js diff --git a/addon-test-support/-private/execution_context/acceptance.js b/addon-test-support/-private/execution_context/acceptance.js index ef17352d..c8941122 100644 --- a/addon-test-support/-private/execution_context/acceptance.js +++ b/addon-test-support/-private/execution_context/acceptance.js @@ -2,7 +2,8 @@ import { run } from '../action'; import { guardMultiple, buildSelector, - findClosestValue + findClosestValue, + getCustomTextFilters } from '../helpers'; import { fillElement, @@ -86,7 +87,12 @@ AcceptanceExecutionContext.prototype = { assertElementExists(selector, options) { /* global find */ - let result = find(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')); + let result = find(selector, options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer')).toArray(); + + Object.values(getCustomTextFilters(options)) + .forEach(customFilter => { + result = result.filter($ele => customFilter($ele.textContent.trim(), options.contains)); + }); if (result.length === 0) { throwBetterError( @@ -96,6 +102,8 @@ AcceptanceExecutionContext.prototype = { { selector } ); } + + return result; }, find(selector, options) { diff --git a/addon-test-support/-private/execution_context/integration.js b/addon-test-support/-private/execution_context/integration.js index e8985d84..b5f8b959 100644 --- a/addon-test-support/-private/execution_context/integration.js +++ b/addon-test-support/-private/execution_context/integration.js @@ -4,7 +4,8 @@ import { run as runAction } from '../action'; import { guardMultiple, buildSelector, - findClosestValue + findClosestValue, + getCustomTextFilters } from '../helpers'; import { fillElement, @@ -100,11 +101,16 @@ IntegrationExecutionContext.prototype = { let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); if (container) { - result = $(selector, container); + result = $(selector, container).toArray(); } else { - result = this.testContext.$(selector); + result = this.testContext.$(selector).toArray(); } + Object.values(getCustomTextFilters(options)) + .forEach(customFilter => { + result = result.filter($ele => customFilter($ele.textContent.trim(), options.contains)); + }); + if (result.length === 0) { throwBetterError( this.pageObjectNode, @@ -113,6 +119,8 @@ IntegrationExecutionContext.prototype = { { selector } ); } + + return result; }, find(selector, options) { diff --git a/addon-test-support/-private/execution_context/native-events-context.js b/addon-test-support/-private/execution_context/native-events-context.js index e345f39b..dc0d1584 100644 --- a/addon-test-support/-private/execution_context/native-events-context.js +++ b/addon-test-support/-private/execution_context/native-events-context.js @@ -12,7 +12,8 @@ import { run } from '../action'; import { guardMultiple, buildSelector, - findClosestValue + findClosestValue, + getCustomTextFilters } from '../helpers'; import { fillElement, @@ -114,7 +115,12 @@ ExecutionContext.prototype = { assertElementExists(selector, options) { let container = options.testContainer || findClosestValue(this.pageObjectNode, 'testContainer'); - let result = this.$(selector, container); + let result = this.$(selector, container).toArray(); + + Object.values(getCustomTextFilters(options)) + .forEach(customFilter => { + result = result.filter($ele => customFilter($ele.textContent.trim(), options.contains)); + }); if (result.length === 0) { throwBetterError( @@ -124,6 +130,8 @@ ExecutionContext.prototype = { { selector } ); } + + return result; }, find(selector, options) { diff --git a/addon-test-support/-private/execution_context/rfc268.js b/addon-test-support/-private/execution_context/rfc268.js index f7ffff9f..22d1aaa8 100644 --- a/addon-test-support/-private/execution_context/rfc268.js +++ b/addon-test-support/-private/execution_context/rfc268.js @@ -4,6 +4,7 @@ import { guardMultiple, buildSelector, findClosestValue, + getCustomTextFilters } from '../helpers'; import { getRootElement, @@ -62,7 +63,12 @@ ExecutionContext.prototype = { }, assertElementExists(selector, options) { - let result = this.getElements(selector, options); + let result = this.getElements(selector, options).toArray(); + + Object.values(getCustomTextFilters(options)) + .forEach(customFilter => { + result = result.filter($ele => customFilter($ele.textContent.trim(), options.contains)); + }); if (result.length === 0) { throwBetterError( @@ -72,6 +78,8 @@ ExecutionContext.prototype = { { selector } ); } + + return result; }, find(selector, options) { diff --git a/addon-test-support/-private/helpers.js b/addon-test-support/-private/helpers.js index dbe05f91..3d514c18 100644 --- a/addon-test-support/-private/helpers.js +++ b/addon-test-support/-private/helpers.js @@ -56,9 +56,7 @@ class Selector { } if (this.targetFilters.contains) { - const exactFilter = this.targetFilters.exact ? 'Exact': ''; - - filters.push(`:contains${exactFilter}("${this.targetFilters.contains}")`); + filters.push(`:contains("${this.targetFilters.contains}")`); } if (typeof this.targetFilters.at === 'number') { @@ -354,3 +352,22 @@ export function getPageObjectDefinition(node){ export function storePageObjectDefinition(node, definition){ Ceibo.meta(node).__poDef__ = definition; } + +export function getCustomTextFilters(obj) { + const requestedCustomFilters = {}; + + Object.keys(obj) + .forEach(filter => { + if (customTextFilters[filter]) { + requestedCustomFilters[filter] = customTextFilters[filter]; + } + }); + + return requestedCustomFilters; +} + +const customTextFilters = { + exact: (a, b) => a === b +} + +export { customTextFilters }; diff --git a/addon-test-support/-private/register-helpers.js b/addon-test-support/-private/register-helpers.js deleted file mode 100644 index 87ca807b..00000000 --- a/addon-test-support/-private/register-helpers.js +++ /dev/null @@ -1,13 +0,0 @@ -import $ from '-jquery'; - -function registerContainsExactHelper() { - $.expr[':'].containsExact = function(a, i, m) { - const textToFind = m[3].replace(/[-[\]{}(')*+?.[,\\^$|#\s]/g, '\\$&'); // escape special character for regex - - return $(a).text().match("^" + textToFind + "$"); - }; -} - -export default function() { - registerContainsExactHelper(); -} diff --git a/addon-test-support/create.js b/addon-test-support/create.js index c80a1da3..045378b9 100644 --- a/addon-test-support/create.js +++ b/addon-test-support/create.js @@ -2,14 +2,9 @@ import Ceibo from 'ceibo'; import { deprecate } from '@ember/application/deprecations'; import { render, setContext, removeContext } from './-private/context'; import { assign, getPageObjectDefinition, isPageObject, storePageObjectDefinition } from './-private/helpers'; -import registerHelpers from './-private/register-helpers'; import { visitable } from './properties/visitable'; import dsl from './-private/dsl'; -// register jQuery helpers initially - -registerHelpers(); - function assignDescriptors(target, source) { Object.getOwnPropertyNames(source).forEach((key) => { const descriptor = Object.getOwnPropertyDescriptor(source, key); diff --git a/addon-test-support/properties/click-on-text.js b/addon-test-support/properties/click-on-text.js index 351629ac..783990b3 100644 --- a/addon-test-support/properties/click-on-text.js +++ b/addon-test-support/properties/click-on-text.js @@ -97,9 +97,9 @@ export function clickOnText(selector, userOptions = {}) { let fullSelector = buildSelector(this, context, selector, options); let container = options.testContainer || findClosestValue(this, 'testContainer'); - context.assertElementExists(fullSelector, options); + const $ele = context.assertElementExists(fullSelector, options); - return context.click(fullSelector, container, options); + return context.click($ele[0], container, options); }); }; } diff --git a/tests/unit/-private/properties/click-on-text-test.ts b/tests/unit/-private/properties/click-on-text-test.ts index 7f85c36f..1cf5078b 100644 --- a/tests/unit/-private/properties/click-on-text-test.ts +++ b/tests/unit/-private/properties/click-on-text-test.ts @@ -247,7 +247,7 @@ moduleForProperty('clickOnText', function(test) { `); - this.adapter.$('button:containsExact("foo")').one('click', function() { + this.adapter.$('button:contains("foo")').one('click', function() { assert.ok(true); });