Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 30 additions & 88 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,102 +21,44 @@ export function configure(options) {
`;
return { content: configFunction };
}
const withinSelectors = {};
Object.keys(queries).forEach(withinQueryName => {

withinSelectors[withinQueryName] =
new Function(`
const els = arguments[0];
if(els.length > 1) {
throw new Error("within() only works with a single element, found " + els.length);
}
const el = els[0];
const args = Array.from(arguments).slice(1);
return window.TestingLibraryDom.within(el).${withinQueryName}.apply(null, args);
`)

});

Object.keys(queries).forEach(queryName => {

module.exports[queryName] = Selector(
new Function(
`
if(!window.tctlReplacer) {
window.tctlReplacer = function tctlReplacer(key, value) {
if (value instanceof RegExp)
return ("__REGEXP " + value.toString());
else if (typeof value === 'function')
return ("__FUNCTION " + value.toString());
else
return value;
}
}
const els = TestingLibraryDom.${queryName}(document.body, ...arguments);
if(!Array.isArray(els)) {
els.setAttribute('data-tctl-args', JSON.stringify(Array.from(arguments), window.tctlReplacer, 0));
els.setAttribute('data-tctl-queryname', '${queryName}');
} else {
els.forEach((el,i) => {
el.setAttribute('data-tctl-args', JSON.stringify(Array.from(arguments), window.tctlReplacer, 0));
el.setAttribute('data-tctl-queryname', '${queryName}');
el.setAttribute('data-tctl-index', i);
});
}
return els;
`,
),
);
(...args) => window.TestingLibraryDom[queryName](document.body, ...args)
, { dependencies: { queryName } });

})
function reviver(key, value) {
if (value.toString().indexOf('__REGEXP ') == 0) {
const m = value.split('__REGEXP ')[1].match(/\/(.*)\/(.*)?/);
return new RegExp(m[1], m[2] || '');
} else
return value;
}

export const within = async selector => {
if (selector instanceof Function) {
return within(selector());
export const within = sel => {
if (sel instanceof Function) {
return within(sel());
}

if (selector.constructor.name === SELECTOR_TYPE) {
const count = await selector.count;
if (count > 1) {
throw new Error(`within() requires a single element, found ${count}`);
}
const el = await selector;
const withinQueryName = el.getAttribute('data-tctl-queryname');

const withinArgs = JSON.parse(el.getAttribute('data-tctl-args'), reviver)
.map(arg => {
if (arg instanceof RegExp) {
return arg.toString();
} else if (arg.toString().indexOf('__FUNCTION ') == 0) {
return (arg.replace('__FUNCTION ', ''))
} else {
return JSON.stringify(arg);
}
}).join(', ');

const withinIndexer = el.hasAttribute('data-tctl-index') ? `[${el.getAttribute('data-tctl-index')}]` : '';

const withinSelectors = {};
Object.keys(queries).forEach(queryName => {
withinSelectors[queryName] = Selector(
new Function(`

const {within, ${withinQueryName}} = TestingLibraryDom;
const el = ${withinQueryName}(document.body, ${withinArgs})${withinIndexer};
return within(el).${queryName}(...arguments);
`
));
});
return withinSelectors;
} else if (typeof (selector) === 'string') {
const sanitizedSelector = selector.replace(/"/g, "'");

const withinSelectors = {};

Object.keys(queries).forEach(queryName => {
withinSelectors[queryName] = Selector(
new Function(
`
const {within} = TestingLibraryDom;
return within(document.querySelector("${sanitizedSelector}")).${queryName}(...arguments);
`),
)
})

return withinSelectors;
if (isSelector(sel)) {
return (sel).addCustomMethods(withinSelectors, { returnDOMNodes: true })
} else if (typeof (sel) === 'string') {
return within(Selector(sel));
} else {
throw new Error(`"within" only accepts a string or another testing-library query as a parameter. ${selector} is not one of those`)
throw new Error(`"within" only accepts a query (getBy, queryBy, etc), string or testcafe Selector`)
}
}

function isSelector(sel) {
return sel.constructor.name === SELECTOR_TYPE;
}

2 changes: 2 additions & 0 deletions test-app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ <h3>getByText within</h3>
<div id="nested2" data-testid="nested2">
<h3>getByText within</h3>
<button onclick="this.innerText = 'Button Clicked'">Button Text</button>
<span>text only in 2nd nested</span>
<span>another thing only in 2nd nested</span>
</div>
</section>
<section>
Expand Down
29 changes: 23 additions & 6 deletions tests/testcafe/within.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,18 @@ test('still works after browser page reload', async t => {


test('works with nested selectors', async t => {
const nested = await within(getByTestId('nested'));
await t.expect(nested.getByText('Button Text').exists).ok()

await t.expect(within(getByTestId('nested')).getByText('Button Text').exists).ok();
});

test('works with nested selector from "All" query with index - regex', async t => {
const nestedDivs = getAllByTestId(/nested/);
await t.expect(nestedDivs.count).eql(2);
const nested = await within(nestedDivs.nth(0));
const nested = within(nestedDivs.nth(1));

await t
.expect(nested.getByText('Button Text').exists).ok()
.expect(nested.getByText('text only in 2nd nested').exists).ok()

await t.expect(nested.getByText('Button Text').exists).ok();
});

test('works with nested selector from "All" query with index - exact:false', async t => {
Expand All @@ -71,14 +72,30 @@ test('works with nested selector from "All" query with index - function', async
await t.expect(nested.getByText('Button Text').exists).ok();
});

test('works on a standard testcafe nested selector', async (t) => {
const nested = Selector('#nested');

await t.expect(within(nested).getByText('Button Text').exists).ok()
});

test('should throw if invalid param', async t => {
let didThrow = false;
try {
await t.expect(within({ 'foo': 'bar' }).getByText('baz').exists).ok();

} catch (e) {
didThrow = true;
}
await t.expect(didThrow).ok();
});

test('should throw error if count > 1', async t => {
const nestedDivs = getAllByTestId(/nested/);

await t.expect(nestedDivs.count).eql(2);
let didThrow = false;
try {
await within(nestedDivs);
await t.expect(within(nestedDivs).getByText('blah'));
} catch (e) {
didThrow = true;
}
Expand Down