blob: 47222c97be25d9ae4a514803b1d9d11631d6c1d5 [file] [log] [blame]
import { expect } from 'chai';
import { PageObjectElement } from './page_object_element';
import { Serializable } from 'puppeteer';
/**
* A test bed for PageObjectElement test cases that must run both in-browser and on Puppeteer.
*
* The methods in this interface allow writing PageObjectElement test cases in an
* environment-independent way. Each environment (i.e. the in-browser and Puppeteer test suites)
* must provide its own implementation.
*/
export interface TestBed {
/**
* Injects the given HTML into a test page, and returns a new PageObjectElement wrapping the
* top-level element in said HTML.
*
* The HTML must have exactly one top-level element.
*
* @example
* "<div>Hello, world!</div>" // Valid example.
* "<span>Hello</span> <span>World!</span>" // Invalid example.
*/
setUpPageObjectElement(html: string): Promise<PageObjectElement>;
/**
* Evaluates the given function inside the test page, passing as the first argument the
* HTMLElement wrapped by the PageObjectElement returned by the most recent call to
* setUpPageObjectElement().
*/
evaluate<T extends Serializable | void = void>(fn: (el: HTMLElement) => T): Promise<T>;
};
/**
* Sets up the PageObjectElement test cases shared by its in-browser and Puppeteer test suites.
*
* Any test cases for behaviors that apply to both the in-browser and Puppeteer environments should
* be place here. Test cases for environment-specific behaviors, if any, should be placed in
* page_object_element_test.ts or page_object_element_puppeteer_test.ts.
*/
export const describePageObjectElement = (testBed: TestBed) => {
it('supports innerText', async () => {
const poe = await testBed.setUpPageObjectElement('<div>Hello, world!</div>');
expect(await poe.innerText).to.equal('Hello, world!');
});
it('supports className', async () => {
const poe = await testBed.setUpPageObjectElement('<div class="hello world"></div>');
expect(await poe.className).to.equal('hello world');
});
it('supports focus', async () => {
const poe = await testBed.setUpPageObjectElement(`<button>Hello, world!</button>`);
const isFocused = () => testBed.evaluate((el) => document.activeElement === el);
expect(await isFocused()).to.be.false;
await poe.focus();
expect(await isFocused()).to.be.true;
});
it('supports click', async () => {
const poe =
await testBed.setUpPageObjectElement(
`<button onclick="this.setAttribute('clicked', true)">Click me!</button>`);
const wasClicked = () => testBed.evaluate((el: HTMLElement) => el.hasAttribute('clicked'));
expect(await wasClicked()).to.be.false;
await poe.click();
expect(await wasClicked()).to.be.true;
});
it('supports hasAttribute', async () => {
let poe = await testBed.setUpPageObjectElement(`<div></div>`);
expect(await poe.hasAttribute('hello')).to.be.false;
poe = await testBed.setUpPageObjectElement(`<div hello></div>`);
expect(await poe.hasAttribute('hello')).to.be.true;
});
it('supports getAttribute', async () => {
let poe = await testBed.setUpPageObjectElement(`<a>Click me!</a>`);
expect(await poe.getAttribute('href')).to.be.null;
poe = await testBed.setUpPageObjectElement(`<a href="/hello-world">Click me!</a>`);
expect(await poe.getAttribute('href')).to.equal('/hello-world');
});
it('supports value', async () => {
const poe = await testBed.setUpPageObjectElement('<input type="text" value="hello"/>');
expect(await poe.value).to.equal('hello');
});
it('supports enterValue', async () => {
const poe = await testBed.setUpPageObjectElement(`
<input type="text"
oninput="this.setAttribute('input-event-dispatched', true)"
onchange="this.setAttribute('change-event-dispatched', true)"/>`);
const wasInputEventDispatched =
() => testBed.evaluate((el: HTMLElement) => el.hasAttribute('input-event-dispatched'));
const wasChangeEventDispatched =
() => testBed.evaluate((el: HTMLElement) => el.hasAttribute('change-event-dispatched'));
expect(await wasInputEventDispatched()).to.be.false;
expect(await wasChangeEventDispatched()).to.be.false;
await poe.enterValue('hello');
expect(await testBed.evaluate((el) => (el as HTMLInputElement).value)).to.equal('hello');
expect(await wasInputEventDispatched()).to.be.true;
expect(await wasChangeEventDispatched()).to.be.true;
});
it('supports typeKey', async () => {
const poe = await testBed.setUpPageObjectElement(`
<input type="text"
onkeydown="this.setAttribute('keydown-event-key', event.key)"
onkeypress="this.setAttribute('keypress-event-key', event.key)"
onkeyup="this.setAttribute('keyup-event-key', event.key)"/>`);
const keydownEventKey =
() => testBed.evaluate((el: HTMLElement) => el.getAttribute('keydown-event-key'));
const keypressEventKey =
() => testBed.evaluate((el: HTMLElement) => el.getAttribute('keypress-event-key'));
const keyupEventKey =
() => testBed.evaluate((el: HTMLElement) => el.getAttribute('keyup-event-key'));
expect(await keydownEventKey()).to.be.null;
expect(await keypressEventKey()).to.be.null;
expect(await keyupEventKey()).to.be.null;
await poe.typeKey('a');
expect(await keydownEventKey()).to.equal('a');
expect(await keypressEventKey()).to.equal('a');
expect(await keyupEventKey()).to.equal('a');
});
it('supports applyFnToDOMNode', async () => {
const poe = await testBed.setUpPageObjectElement('<div>Hello, world!</div>');
const result =
await poe.applyFnToDOMNode(
(el, prefix, suffix) => `${prefix}${el.innerText}${suffix}`,
'The contents are: "',
'". That is all.');
expect(result).to.equal('The contents are: "Hello, world!". That is all.');
});
describe('query selector functions', () => {
let poe: PageObjectElement;
beforeEach(async () => {
poe = await testBed.setUpPageObjectElement(`
<div>
<span class=salutation>Hello</span>,
<span class=name>World</span>!
</div>
`);
});
it('supports selectOnePOE', async () => {
expect(await poe.selectOnePOE('p')).to.be.null;
expect(await poe.selectOnePOE('span')).to.not.be.null;
expect(await (await poe.selectOnePOE('span.name'))!.innerText).to.equal('World');
});
it('supports selectAllPOE', async () => {
const innerTexts =
async (pageObjectElements: Promise<PageObjectElement[]>) =>
await Promise.all((await pageObjectElements).map((el) => el.innerText));
expect(await poe.selectAllPOE('p')).to.have.length(0);
expect(await poe.selectAllPOE('span')).to.have.length(2);
expect(await innerTexts(poe.selectAllPOE('span'))).to.deep.equal(['Hello', 'World']);
});
it('supports selectOnePOEThenApplyFn', async () => {
expect(await poe.selectOnePOEThenApplyFn<string>('span.name', (el) => el.innerText))
.to.equal('World');
await expectError(
() => poe.selectOnePOEThenApplyFn<string>('unknown-element', async (el) => ''),
'selector "unknown-element" did not match any elements');
});
it('supports selectOneDOMNodeThenApplyFn', async () => {
expect(await poe.selectOneDOMNodeThenApplyFn('span.name', (el) => el.innerText))
.to.equal('World');
await expectError(
() => poe.selectOneDOMNodeThenApplyFn<string>('unknown-element', (el) => ''),
'selector "unknown-element" did not match any elements');
});
it('supports selectAllPOEThenMap', async () => {
expect(await poe.selectAllPOEThenMap('span', (el) => el.innerText))
.to.deep.equal(['Hello', 'World']);
});
it('supports selectAllPOEThenForEach', async () => {
const text: string[] = [];
await poe.selectAllPOEThenForEach('span', async (el) => {
text.push(await el.innerText);
});
expect(text).to.deep.equal(['Hello', 'World']);
});
it('supports selectAllPOEThenFind', async () => {
expect(
await poe.selectAllPOEThenFind('span', async (el) => (await el.className) === 'unknown'))
.to.be.null;
const span =
await poe.selectAllPOEThenFind('span', async (el) => (await el.className) === 'name');
expect(await span!.innerText).to.equal('World');
});
});
};
const expectError = async <T>(fn: () => Promise<T>, expectedMessage: string) => {
try {
await fn();
} catch (e) {
expect(e.message).to.equal(expectedMessage);
return;
}
expect.fail('expection not thrown');
};