@web/test-runner-commands
Version:
Web test runner commands
173 lines • 7.21 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.snapshotPlugin = snapshotPlugin;
const fs_1 = __importDefault(require("fs"));
const mkdirp_1 = __importDefault(require("mkdirp"));
const path_1 = __importDefault(require("path"));
const util_1 = require("util");
const readFile = (0, util_1.promisify)(fs_1.default.readFile);
const writeFile = (0, util_1.promisify)(fs_1.default.writeFile);
const unlink = (0, util_1.promisify)(fs_1.default.unlink);
const access = (0, util_1.promisify)(fs_1.default.access);
async function fileExists(filePath) {
try {
await access(filePath);
return true;
}
catch {
return false;
}
}
function isObject(payload) {
return payload != null && typeof payload === 'object';
}
function isSaveSnapshotPayload(payload) {
if (!isObject(payload))
throw new Error('You must provide a payload object');
if (typeof payload.name !== 'string')
throw new Error('You must provide a path option');
if (payload.content !== undefined && typeof payload.content !== 'string')
throw new Error('You must provide a content option');
return true;
}
function getSnapshotPath(testFile) {
const testDir = path_1.default.dirname(testFile);
const testFileName = path_1.default.basename(testFile);
const ext = path_1.default.extname(testFileName);
const fileWithoutExt = testFileName.substring(0, testFileName.length - ext.length);
return path_1.default.join(testDir, '__snapshots__', `${fileWithoutExt}.snap.js`);
}
class SnapshotStore {
snapshots = new Map();
sessionToSnapshotPath = new Map();
readOperations = new Map();
async get(testFilePath) {
const snapshotPath = getSnapshotPath(testFilePath);
if (this.readOperations.has(snapshotPath)) {
// something else is already reading, wait for it
await this.readOperations.get(snapshotPath)?.promise;
}
const cachedContent = this.snapshots.get(snapshotPath);
if (cachedContent) {
// return from cache
return cachedContent;
}
const promiseObj = { resolve: () => { }, promise: Promise.resolve() };
promiseObj.promise = new Promise(resolve => {
promiseObj.resolve = resolve;
});
this.readOperations.set(testFilePath, promiseObj);
// store in cache
const content = (await fileExists(snapshotPath))
? await readFile(snapshotPath, 'utf-8')
: '/* @web/test-runner snapshot v1 */\nexport const snapshots = {};\n\n';
this.snapshots.set(snapshotPath, content);
// resolve read promise to let others who are waiting continue
this.readOperations.get(snapshotPath)?.resolve();
this.readOperations.delete(snapshotPath);
return content;
}
async saveSnapshot(sessionId, testFilePath, name, updatedSnapshot) {
const snapshotPath = getSnapshotPath(testFilePath);
const nameStr = JSON.stringify(name);
const startMarker = `snapshots[${nameStr}]`;
const endMarker = `/* end snapshot ${name} */\n\n`;
const replacement = updatedSnapshot
? `${startMarker} = \n\`${updatedSnapshot}\`;\n${endMarker}`
: '';
const content = await this.get(testFilePath);
let updatedContent;
const startIndex = content.indexOf(startMarker);
if (startIndex !== -1) {
// replace existing snapshot
const endIndex = content.indexOf(endMarker);
if (endIndex === -1) {
throw new Error('Missing snapshot end marker');
}
const beforeReplace = content.substring(0, startIndex);
const afterReplace = content.substring(endIndex + endMarker.length);
updatedContent = `${beforeReplace}${replacement}${afterReplace}`;
}
else {
// add new snapshot
updatedContent = `${content}${replacement}`;
}
if (updatedContent === content) {
// snapshot did not actually change, avoid marking snapshot as dirty
return;
}
this.sessionToSnapshotPath.set(sessionId, snapshotPath);
this.snapshots.set(snapshotPath, updatedContent);
}
getSnapshotPathForSession(sessionId) {
return this.sessionToSnapshotPath.get(sessionId);
}
async writeSnapshot(snapshotPath) {
const updatedContent = this.snapshots.get(snapshotPath);
if (!updatedContent) {
throw new Error('Unexpected error while writing snapshots, could not find snapshot content.');
}
if (updatedContent.includes('/* end snapshot')) {
// update or create snapshot
const fileDir = path_1.default.dirname(snapshotPath);
await (0, mkdirp_1.default)(fileDir);
await writeFile(snapshotPath, updatedContent);
}
else {
// snapshot file is empty, remove it
if (await fileExists(snapshotPath)) {
await unlink(snapshotPath);
}
}
}
}
function snapshotPlugin(config) {
const updateSnapshots = config && config.updateSnapshots;
const snapshots = new SnapshotStore();
const writePromises = new Set();
return {
name: 'file-commands',
serverStart({ webSockets }) {
webSockets.on('message', async ({ data }) => {
const { type, sessionId } = data;
if (type === 'wtr-session-finished') {
if (typeof sessionId !== 'string') {
throw new Error('Missing session id in wtr-session-finished event');
}
const snapshotPath = snapshots.getSnapshotPathForSession(sessionId);
if (!snapshotPath) {
return;
}
const writePromise = snapshots.writeSnapshot(snapshotPath);
writePromises.add(writePromise);
await writePromise;
writePromises.delete(writePromise);
}
});
},
async serverStop() {
// ensure all write operations are finished
await Promise.all([...writePromises]);
},
async executeCommand({ command, payload, session }) {
if (command === 'get-snapshot-config') {
return { updateSnapshots };
}
if (command === 'get-snapshots') {
const content = await snapshots.get(session.testFile);
return { content };
}
if (command === 'save-snapshot') {
if (!isSaveSnapshotPayload(payload)) {
throw new Error('Invalid save snapshot payload');
}
await snapshots.saveSnapshot(session.id, session.testFile, payload.name, payload.content);
return true;
}
},
};
}
//# sourceMappingURL=snapshotPlugin.js.map