fix(store): support text and binary values (#21006)

This commit is contained in:
Yury Semikhatsky 2023-03-01 08:49:31 -08:00 committed by GitHub
parent d12d35f124
commit 60e5a93832
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 112 additions and 27 deletions

View File

@ -14,6 +14,10 @@
* limitations under the License.
*/
export function isJsonMimeType(mimeType: string) {
return !!mimeType.match(/^(application\/json|application\/.*?\+json|text\/(x-)?json)(;\s*charset=.*)?$/);
}
export function isTextualMimeType(mimeType: string) {
return !!mimeType.match(/^(text\/.*?|application\/(json|(x-)?javascript|xml.*?|ecmascript|graphql|x-www-form-urlencoded)|image\/svg(\+xml)?|application\/.*?(\+json|\+xml))(;\s*charset=.*)?$/);
}

View File

@ -18,6 +18,8 @@ import fs from 'fs';
import path from 'path';
import type { TestStore } from '../types/test';
import { currentConfig } from './common/globals';
import { mime } from 'playwright-core/lib/utilsBundle';
import { isJsonMimeType, isString, isTextualMimeType } from 'playwright-core/lib/utils';
class JsonStore implements TestStore {
async delete(name: string) {
@ -28,8 +30,13 @@ class JsonStore implements TestStore {
async get<T>(name: string) {
const file = this.path(name);
try {
const data = await fs.promises.readFile(file, 'utf-8');
return JSON.parse(data) as T;
const type = contentType(name);
if (type === 'binary')
return await fs.promises.readFile(file) as T;
const text = await fs.promises.readFile(file, 'utf-8');
if (type === 'json')
return JSON.parse(text) as T;
return text as T;
} catch (e) {
return undefined;
}
@ -52,10 +59,39 @@ class JsonStore implements TestStore {
await fs.promises.rm(file, { force: true });
return;
}
const data = JSON.stringify(value, undefined, 2);
let data: string | Buffer = '';
switch (contentType(name)) {
case 'json': {
if (Buffer.isBuffer(value))
throw new Error('JSON value must be an Object');
data = JSON.stringify(value, undefined, 2);
break;
}
case 'text': {
if (!isString(value))
throw new Error('Textual value must be a string');
data = value as string;
break;
}
case 'binary': {
if (!Buffer.isBuffer(value))
throw new Error('Binary value must be a Buffer');
data = value;
break;
}
}
await fs.promises.mkdir(path.dirname(file), { recursive: true });
await fs.promises.writeFile(file, data);
}
}
function contentType(name: string): 'json'|'text'|'binary' {
const mimeType = mime.getType(path.basename(name)) ?? 'application/octet-string';
if (isJsonMimeType(mimeType))
return 'json';
if (isTextualMimeType(mimeType))
return 'text';
return 'binary';
}
export const store = new JsonStore();

View File

@ -27,15 +27,15 @@ test('should provide store fixture', async ({ runInlineTest }) => {
import { test, store, expect } from '@playwright/test';
test('should store number', async ({ }) => {
expect(store).toBeTruthy();
expect(await store.get('number')).toBe(undefined);
await store.set('number', 2022)
expect(await store.get('number')).toBe(2022);
expect(await store.get('number.json')).toBe(undefined);
await store.set('number.json', 2022)
expect(await store.get('number.json')).toBe(2022);
});
test('should store object', async ({ }) => {
expect(store).toBeTruthy();
expect(await store.get('object')).toBe(undefined);
await store.set('object', { 'a': 2022 })
expect(await store.get('object')).toEqual({ 'a': 2022 });
expect(await store.get('object.json')).toBe(undefined);
await store.set('object.json', { 'a': 2022 })
expect(await store.get('object.json')).toEqual({ 'a': 2022 });
});
`,
}, { workers: 1 });
@ -63,27 +63,27 @@ test('should share store state between project setup and tests', async ({ runInl
'store.setup.ts': `
import { test, store, expect } from '@playwright/test';
test('should initialize store', async ({ }) => {
expect(await store.get('number')).toBe(undefined);
await store.set('number', 2022)
expect(await store.get('number')).toBe(2022);
expect(await store.get('number.json')).toBe(undefined);
await store.set('number.json', 2022)
expect(await store.get('number.json')).toBe(2022);
expect(await store.get('object')).toBe(undefined);
await store.set('object', { 'a': 2022 })
expect(await store.get('object')).toEqual({ 'a': 2022 });
expect(await store.get('object.json')).toBe(undefined);
await store.set('object.json', { 'a': 2022 })
expect(await store.get('object.json')).toEqual({ 'a': 2022 });
});
`,
'a.test.ts': `
import { test, store, expect } from '@playwright/test';
test('should get data from setup', async ({ }) => {
expect(await store.get('number')).toBe(2022);
expect(await store.get('object')).toEqual({ 'a': 2022 });
expect(await store.get('number.json')).toBe(2022);
expect(await store.get('object.json')).toEqual({ 'a': 2022 });
});
`,
'b.test.ts': `
import { test, store, expect } from '@playwright/test';
test('should get data from setup', async ({ }) => {
expect(await store.get('number')).toBe(2022);
expect(await store.get('object')).toEqual({ 'a': 2022 });
expect(await store.get('number.json')).toBe(2022);
expect(await store.get('object.json')).toEqual({ 'a': 2022 });
});
`,
}, { workers: 1 });
@ -99,17 +99,17 @@ test('should persist store state between project runs', async ({ runInlineTest }
'a.test.ts': `
import { test, store, expect } from '@playwright/test';
test('should have no data on first run', async ({ }) => {
expect(await store.get('number')).toBe(undefined);
await store.set('number', 2022)
expect(await store.get('object')).toBe(undefined);
await store.set('object', { 'a': 2022 })
expect(await store.get('number.json')).toBe(undefined);
await store.set('number.json', 2022)
expect(await store.get('object.json')).toBe(undefined);
await store.set('object.json', { 'a': 2022 })
});
`,
'b.test.ts': `
import { test, store, expect } from '@playwright/test';
test('should get data from previous run', async ({ }) => {
expect(await store.get('number')).toBe(2022);
expect(await store.get('object')).toEqual({ 'a': 2022 });
expect(await store.get('number.json')).toBe(2022);
expect(await store.get('object.json')).toEqual({ 'a': 2022 });
});
`,
};
@ -152,13 +152,13 @@ test('should load context storageState from store', async ({ runInlineTest, serv
expect(await store.get('user')).toBe(undefined);
await page.goto('${server.PREFIX}/setcookie.html');
const state = await page.context().storageState();
await store.set('user', state);
await store.set('user.json', state);
});
`,
'a.test.ts': `
import { test, store, expect } from '@playwright/test';
test.use({
storageState: async ({}, use) => use(store.get('user'))
storageState: async ({}, use) => use(store.get('user.json'))
})
test('should get data from setup', async ({ page }) => {
await page.goto('${server.EMPTY_PAGE}');
@ -290,3 +290,48 @@ test('should delete value', async ({ runInlineTest }) => {
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
});
test('should support text, json and binary values', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
import { test, store, expect } from '@playwright/test';
test('json', async ({ }) => {
await store.set('key.json', {'a': 2023});
expect(await store.get('key.json')).toEqual({ 'a': 2023 });
});
test('text', async ({ }) => {
await store.set('key.txt', 'Hello');
expect(await store.get('key.txt')).toEqual('Hello');
});
test('binary', async ({ }) => {
const buf = Buffer.alloc(256);
for (let i = 0; i < 256; i++)
buf[i] = i;
await store.set('key.png', buf);
expect(await store.get('key.png')).toEqual(buf);
});
`,
}, { workers: 1 });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(3);
});
test('should throw on unsupported value type for given key extension', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
import { test, store, expect } from '@playwright/test';
test('json', async ({ }) => {
const buf = Buffer.alloc(5);
await store.set('key.json', buf);
});
test('text', async ({ }) => {
await store.set('key.txt', {});
});
test('binary', async ({ }) => {
await store.set('key.png', {});
});
`,
}, { workers: 1 });
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(3);
});