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
2 changes: 1 addition & 1 deletion packages/injected/src/selectorUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function elementText(cache: Map<Element | ShadowRoot, ElementText>, root:
value = { full: '', normalized: '', immediate: [] };
if (!shouldSkipForTextMatching(root)) {
let currentImmediate = '';
if ((root instanceof HTMLInputElement) && (root.type === 'submit' || root.type === 'button')) {
if ((root instanceof HTMLInputElement) && (root.type === 'submit' || root.type === 'button' || root.type === 'reset')) {
value = { full: root.value, normalized: normalizeWhiteSpace(root.value), immediate: [root.value] };
} else {
for (let child = root.firstChild; child; child = child.nextSibling) {
Expand Down
5 changes: 4 additions & 1 deletion packages/isomorphic/urlMatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,10 @@ function resolveGlobBase(baseURL: string | undefined, match: string): string {
let resolved = result.resolved;
for (const [token, original] of tokenMap) {
const normalize = result.caseInsensitivePart?.includes(token);
resolved = resolved.replace(token, normalize ? original.toLowerCase() : original);
const replacement = normalize ? original.toLowerCase() : original;
// '$$', '$&', '$`' and "$'" are special in String.prototype.replace with a string argument.
// Instead, use the function argument form that treats the string argument literally.
resolved = resolved.replace(token, () => replacement);
}
match = resolved;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/server/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,7 @@ function serializePostData(params: channels.APIRequestContextFetchParams, header
for (const field of params.multipartData) {
if (field.file)
formData.addFileField(field.name, field.file);
else if (field.value)
else if (field.value !== undefined)
formData.addField(field.name, field.value);
}
setHeader(headers, 'content-type', formData.contentTypeHeader(), true);
Expand Down
6 changes: 3 additions & 3 deletions packages/playwright-core/src/server/screenshotter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ function trimClipToSize(clip: types.Rect, size: types.Size): types.Rect {
y: Math.max(0, Math.min(clip.y + clip.height, size.height))
};
const result = { x: p1.x, y: p1.y, width: p2.x - p1.x, height: p2.y - p1.y };
assert(result.width && result.height, 'Clipped area is either empty or outside the resulting image');
assert(result.width > 0 && result.height > 0, 'Clipped area is either empty or outside the resulting image');
return result;
}

Expand All @@ -376,8 +376,8 @@ export function validateScreenshotOptions(options: ScreenshotOptions): 'png' | '
assert(typeof options.clip.y === 'number', 'Expected options.clip.y to be a number but found ' + (typeof options.clip.y));
assert(typeof options.clip.width === 'number', 'Expected options.clip.width to be a number but found ' + (typeof options.clip.width));
assert(typeof options.clip.height === 'number', 'Expected options.clip.height to be a number but found ' + (typeof options.clip.height));
assert(options.clip.width !== 0, 'Expected options.clip.width not to be 0.');
assert(options.clip.height !== 0, 'Expected options.clip.height not to be 0.');
assert(options.clip.width > 0, 'Expected options.clip.width to be greater than 0.');
assert(options.clip.height > 0, 'Expected options.clip.height to be greater than 0.');
}
return format;
}
5 changes: 3 additions & 2 deletions packages/playwright/src/matchers/toMatchSnapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,10 @@ class SnapshotHelper {
handleMissing(actual: Buffer | string): MatcherResult<string, string> {
const attachments: MatcherAttachment[] = [];
const isWriteMissingMode = this.updateSnapshots !== 'none';
if (isWriteMissingMode)
if (isWriteMissingMode) {
writeFileSync(this.expectedPath, actual);
attachments.push({ name: addSuffixToFilePath(this.attachmentBaseName, '-expected'), contentType: this.mimeType, path: this.expectedPath });
attachments.push({ name: addSuffixToFilePath(this.attachmentBaseName, '-expected'), contentType: this.mimeType, path: this.expectedPath });
}
writeFileSync(this.actualPath, actual);
attachments.push({ name: addSuffixToFilePath(this.attachmentBaseName, '-actual'), contentType: this.mimeType, path: this.actualPath });
const message = `A snapshot doesn't exist at ${this.expectedPath}${isWriteMissingMode ? ', writing actual.' : '.'}`;
Expand Down
2 changes: 2 additions & 0 deletions tests/library/browsercontext-fetch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,7 @@ it('should support multipart/form-data', async function({ context, server }) {
context.request.post(server.EMPTY_PAGE, {
multipart: {
firstName: 'John',
middleName: '',
lastName: 'Doe',
file
}
Expand All @@ -1056,6 +1057,7 @@ it('should support multipart/form-data', async function({ context, server }) {
expect(serverRequest.method).toBe('POST');
expect(serverRequest.headers['content-type']).toContain('multipart/form-data');
expect(fields['firstName']).toBe('John');
expect(fields['middleName']).toBe('');
expect(fields['lastName']).toBe('Doe');
expect(files['file'].originalFilename).toBe(file.name);
expect(files['file'].mimetype).toBe(file.mimeType);
Expand Down
5 changes: 5 additions & 0 deletions tests/page/interception.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ it('should work with glob', async () => {
expect(urlMatches('http://playwright.dev', 'http://playwright.dev/?x=y', '?x=y')).toBeTruthy();
expect(urlMatches('http://playwright.dev/foo/', 'http://playwright.dev/foo/bar?x=y', './bar?x=y')).toBeTruthy();

// '$$', '$&', '$`' and "$'" are special in String.prototype.replace with a string argument.
expect(urlMatches(undefined, 'http://playwright.dev/foo$$bar', 'http://playwright.dev/foo$$bar')).toBeTruthy();
expect(urlMatches(undefined, 'http://playwright.dev/a$&b', 'http://playwright.dev/a$&b')).toBeTruthy();
expect(urlMatches('http://playwright.dev', 'http://playwright.dev/p$$q', './p$$q')).toBeTruthy();

// Case insensitive matching
expect(urlMatches(undefined, 'https://playwright.dev/fooBAR', 'HtTpS://pLaYwRiGhT.dEv/fooBAR')).toBeTruthy();
expect(urlMatches('http://ignored', 'https://playwright.dev/fooBAR', 'HtTpS://pLaYwRiGhT.dEv/fooBAR')).toBeTruthy();
Expand Down
14 changes: 14 additions & 0 deletions tests/page/page-screenshot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,20 @@ it.describe('page screenshot', () => {
expect(screenshotError.message).toContain('Clipped area is either empty or outside the resulting image');
});

it('should throw on a negative clip size', async ({ page, server }) => {
await page.setViewportSize({ width: 500, height: 500 });
await page.goto(server.PREFIX + '/grid.html');
const screenshotError = await page.screenshot({
clip: {
x: 50,
y: 50,
width: -100,
height: 100
}
}).catch(error => error);
expect(screenshotError.message).toContain('Expected options.clip.width to be greater than 0');
});

it('should run in parallel', async ({ page, server }) => {
await page.setViewportSize({ width: 500, height: 500 });
await page.goto(server.PREFIX + '/grid.html');
Expand Down
5 changes: 3 additions & 2 deletions tests/page/selectors-text.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,10 +362,11 @@ it('should skip head, script and style', async ({ page }) => {
}
});

it('should match input[type=button|submit]', async ({ page }) => {
await page.setContent(`<input type="submit" value="hello"><input type="button" value="world">`);
it('should match input[type=button|submit|reset]', async ({ page }) => {
await page.setContent(`<input type="submit" value="hello"><input type="button" value="world"><input type="reset" value="clear">`);
expect(await page.$eval(`text=hello`, e => e.outerHTML)).toBe('<input type="submit" value="hello">');
expect(await page.$eval(`text=world`, e => e.outerHTML)).toBe('<input type="button" value="world">');
expect(await page.$eval(`text=clear`, e => e.outerHTML)).toBe('<input type="reset" value="clear">');
});

it('should work for open shadow roots', async ({ page, server }) => {
Expand Down
19 changes: 19 additions & 0 deletions tests/playwright-test/golden.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,25 @@ test('should generate separate actual results for repeating names', async ({ run
]);
});

test('should not attach a missing expected snapshot when update-snapshots is none', async ({ runInlineTest }) => {
const result = await runInlineTest({
...files,
'a.spec.js': `
const { test, expect } = require('./helper');
test.afterEach(async ({}, testInfo) => {
console.log('## ' + JSON.stringify(testInfo.attachments.map(a => a.name)));
});
test('is a test', ({}) => {
expect.soft('a').toMatchSnapshot('foo.txt');
});
`
}, { 'update-snapshots': 'none' });
expect(result.exitCode).toBe(1);
const names = result.output.split('\n').filter(l => l.startsWith('## ')).map(l => JSON.parse(l.substring(3)))[0];
expect(names).toContain('foo-actual.txt');
expect(names).not.toContain('foo-expected.txt');
});

test('should compile with different option combinations', async ({ runTSC }) => {
const result = await runTSC({
'a.spec.ts': `
Expand Down
2 changes: 1 addition & 1 deletion tests/playwright-test/to-have-screenshot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ test('should fail with proper error when unsupported argument is given', async (
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(1);
expect(result.output).toContain(`Expected options.clip.width not to be 0`);
expect(result.output).toContain(`Expected options.clip.width to be greater than 0`);
});

test('should have scale:css by default', async ({ runInlineTest }, testInfo) => {
Expand Down
Loading