fix(expect): toHaveText, toContainText and toHaveTitle normalize whitespace (#8929)
This commit is contained in:
parent
c58f34fb2e
commit
b8a46580dd
|
|
@ -15,12 +15,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Locator, Page } from '../../..';
|
import { Locator, Page } from '../../..';
|
||||||
import { constructURLBasedOnBaseURL } from '../../utils/utils';
|
import { constructURLBasedOnBaseURL, isString } from '../../utils/utils';
|
||||||
import { currentTestInfo } from '../globals';
|
import { currentTestInfo } from '../globals';
|
||||||
import type { Expect } from '../types';
|
import type { Expect } from '../types';
|
||||||
import { toBeTruthy } from './toBeTruthy';
|
import { toBeTruthy } from './toBeTruthy';
|
||||||
import { toEqual } from './toEqual';
|
import { toEqual } from './toEqual';
|
||||||
import { toMatchText } from './toMatchText';
|
import { normalizeWhiteSpace, toMatchText } from './toMatchText';
|
||||||
|
|
||||||
export function toBeChecked(
|
export function toBeChecked(
|
||||||
this: ReturnType<Expect['getState']>,
|
this: ReturnType<Expect['getState']>,
|
||||||
|
|
@ -118,7 +118,7 @@ export function toContainText(
|
||||||
if (options?.useInnerText)
|
if (options?.useInnerText)
|
||||||
return await locator.innerText({ timeout });
|
return await locator.innerText({ timeout });
|
||||||
return await locator.textContent() || '';
|
return await locator.textContent() || '';
|
||||||
}, expected, { ...options, matchSubstring: true });
|
}, expected, { ...options, matchSubstring: true, normalizeWhiteSpace: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveAttribute(
|
export function toHaveAttribute(
|
||||||
|
|
@ -202,20 +202,23 @@ export function toHaveText(
|
||||||
this: ReturnType<Expect['getState']>,
|
this: ReturnType<Expect['getState']>,
|
||||||
locator: Locator,
|
locator: Locator,
|
||||||
expected: string | RegExp | (string | RegExp)[],
|
expected: string | RegExp | (string | RegExp)[],
|
||||||
options?: { timeout?: number, useInnerText?: boolean },
|
options: { timeout?: number, useInnerText?: boolean } = {},
|
||||||
) {
|
) {
|
||||||
if (Array.isArray(expected)) {
|
if (Array.isArray(expected)) {
|
||||||
|
const expectedArray = expected.map(e => isString(e) ? normalizeWhiteSpace(e) : e);
|
||||||
return toEqual.call(this, 'toHaveText', locator, 'Locator', async () => {
|
return toEqual.call(this, 'toHaveText', locator, 'Locator', async () => {
|
||||||
return locator.evaluateAll((ee, useInnerText) => {
|
const texts = await locator.evaluateAll((ee, useInnerText) => {
|
||||||
return ee.map(e => useInnerText ? (e as HTMLElement).innerText : e.textContent || '');
|
return ee.map(e => useInnerText ? (e as HTMLElement).innerText : e.textContent || '');
|
||||||
}, options?.useInnerText);
|
}, options?.useInnerText);
|
||||||
}, expected, options);
|
// Normalize those values that have string expectations.
|
||||||
|
return texts.map((s, index) => isString(expectedArray[index]) ? normalizeWhiteSpace(s) : s);
|
||||||
|
}, expectedArray, options);
|
||||||
} else {
|
} else {
|
||||||
return toMatchText.call(this, 'toHaveText', locator, 'Locator', async timeout => {
|
return toMatchText.call(this, 'toHaveText', locator, 'Locator', async timeout => {
|
||||||
if (options?.useInnerText)
|
if (options?.useInnerText)
|
||||||
return await locator.innerText({ timeout });
|
return await locator.innerText({ timeout });
|
||||||
return await locator.textContent() || '';
|
return await locator.textContent() || '';
|
||||||
}, expected, options);
|
}, expected, { ...options, normalizeWhiteSpace: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -223,11 +226,11 @@ export function toHaveTitle(
|
||||||
this: ReturnType<Expect['getState']>,
|
this: ReturnType<Expect['getState']>,
|
||||||
page: Page,
|
page: Page,
|
||||||
expected: string | RegExp,
|
expected: string | RegExp,
|
||||||
options?: { timeout?: number },
|
options: { timeout?: number } = {},
|
||||||
) {
|
) {
|
||||||
return toMatchText.call(this, 'toHaveTitle', page, 'Page', async () => {
|
return toMatchText.call(this, 'toHaveTitle', page, 'Page', async () => {
|
||||||
return await page.title();
|
return await page.title();
|
||||||
}, expected, options);
|
}, expected, { ...options, normalizeWhiteSpace: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toHaveURL(
|
export function toHaveURL(
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import {
|
||||||
printReceivedStringContainExpectedResult,
|
printReceivedStringContainExpectedResult,
|
||||||
printReceivedStringContainExpectedSubstring
|
printReceivedStringContainExpectedSubstring
|
||||||
} from 'expect/build/print';
|
} from 'expect/build/print';
|
||||||
|
import { isString } from '../../utils/utils';
|
||||||
import { currentTestInfo } from '../globals';
|
import { currentTestInfo } from '../globals';
|
||||||
import type { Expect } from '../types';
|
import type { Expect } from '../types';
|
||||||
import { expectType, pollUntilDeadline } from '../util';
|
import { expectType, pollUntilDeadline } from '../util';
|
||||||
|
|
@ -30,7 +30,7 @@ export async function toMatchText(
|
||||||
receiverType: string,
|
receiverType: string,
|
||||||
query: (timeout: number) => Promise<string>,
|
query: (timeout: number) => Promise<string>,
|
||||||
expected: string | RegExp,
|
expected: string | RegExp,
|
||||||
options: { timeout?: number, matchSubstring?: boolean } = {},
|
options: { timeout?: number, matchSubstring?: boolean, normalizeWhiteSpace?: boolean } = {},
|
||||||
) {
|
) {
|
||||||
const testInfo = currentTestInfo();
|
const testInfo = currentTestInfo();
|
||||||
if (!testInfo)
|
if (!testInfo)
|
||||||
|
|
@ -59,9 +59,13 @@ export async function toMatchText(
|
||||||
|
|
||||||
let received: string;
|
let received: string;
|
||||||
let pass = false;
|
let pass = false;
|
||||||
|
if (options.normalizeWhiteSpace && isString(expected))
|
||||||
|
expected = normalizeWhiteSpace(expected);
|
||||||
|
|
||||||
await pollUntilDeadline(testInfo, async remainingTime => {
|
await pollUntilDeadline(testInfo, async remainingTime => {
|
||||||
received = await query(remainingTime);
|
received = await query(remainingTime);
|
||||||
|
if (options.normalizeWhiteSpace && isString(expected))
|
||||||
|
received = normalizeWhiteSpace(received);
|
||||||
if (options.matchSubstring)
|
if (options.matchSubstring)
|
||||||
pass = received.includes(expected as string);
|
pass = received.includes(expected as string);
|
||||||
else if (typeof expected === 'string')
|
else if (typeof expected === 'string')
|
||||||
|
|
@ -112,3 +116,7 @@ export async function toMatchText(
|
||||||
|
|
||||||
return { message, pass };
|
return { message, pass };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeWhiteSpace(s: string) {
|
||||||
|
return s.trim().replace(/\s+/g, ' ');
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,8 +124,8 @@ test('should support toHaveTitle', async ({ runInlineTest }) => {
|
||||||
const { test } = pwt;
|
const { test } = pwt;
|
||||||
|
|
||||||
test('pass', async ({ page }) => {
|
test('pass', async ({ page }) => {
|
||||||
await page.setContent('<title>Hello</title>');
|
await page.setContent('<title> Hello world</title>');
|
||||||
await expect(page).toHaveTitle('Hello');
|
await expect(page).toHaveTitle('Hello world');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fail', async ({ page }) => {
|
test('fail', async ({ page }) => {
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,12 @@ test('should support toHaveText w/ regex', async ({ runInlineTest }) => {
|
||||||
const { test } = pwt;
|
const { test } = pwt;
|
||||||
|
|
||||||
test('pass', async ({ page }) => {
|
test('pass', async ({ page }) => {
|
||||||
await page.setContent('<div id=node>Text content</div>');
|
await page.setContent('<div id=node>Text content</div>');
|
||||||
const locator = page.locator('#node');
|
const locator = page.locator('#node');
|
||||||
await expect(locator).toHaveText(/Text/);
|
await expect(locator).toHaveText(/Text/);
|
||||||
|
|
||||||
|
// Should not normalize whitespace.
|
||||||
|
await expect(locator).toHaveText(/Text content/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fail', async ({ page }) => {
|
test('fail', async ({ page }) => {
|
||||||
|
|
@ -50,15 +53,18 @@ test('should support toHaveText w/ text', async ({ runInlineTest }) => {
|
||||||
const { test } = pwt;
|
const { test } = pwt;
|
||||||
|
|
||||||
test('pass', async ({ page }) => {
|
test('pass', async ({ page }) => {
|
||||||
await page.setContent('<div id=node>Text content</div>');
|
await page.setContent('<div id=node><span></span>Text \\ncontent </div>');
|
||||||
const locator = page.locator('#node');
|
const locator = page.locator('#node');
|
||||||
await expect(locator).toHaveText('Text content');
|
// Should normalize whitespace.
|
||||||
|
await expect(locator).toHaveText('Text content');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('pass contain', async ({ page }) => {
|
test('pass contain', async ({ page }) => {
|
||||||
await page.setContent('<div id=node>Text content</div>');
|
await page.setContent('<div id=node>Text content</div>');
|
||||||
const locator = page.locator('#node');
|
const locator = page.locator('#node');
|
||||||
await expect(locator).toContainText('Text');
|
await expect(locator).toContainText('Text');
|
||||||
|
// Should normalize whitespace.
|
||||||
|
await expect(locator).toContainText(' Text content\\n ');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fail', async ({ page }) => {
|
test('fail', async ({ page }) => {
|
||||||
|
|
@ -84,9 +90,10 @@ test('should support toHaveText w/ array', async ({ runInlineTest }) => {
|
||||||
const { test } = pwt;
|
const { test } = pwt;
|
||||||
|
|
||||||
test('pass', async ({ page }) => {
|
test('pass', async ({ page }) => {
|
||||||
await page.setContent('<div>Text 1</div><div>Text 2a</div>');
|
await page.setContent('<div>Text \\n1</div><div>Text 2a</div>');
|
||||||
const locator = page.locator('div');
|
const locator = page.locator('div');
|
||||||
await expect(locator).toHaveText(['Text 1', /Text \\d+a/]);
|
// Should only normalize whitespace in the first item.
|
||||||
|
await expect(locator).toHaveText(['Text 1', /Text \\d+a/]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fail', async ({ page }) => {
|
test('fail', async ({ page }) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue