mirror of
https://github.com/penpot/penpot.git
synced 2026-03-21 01:43:43 +00:00
200 lines
5.2 KiB
TypeScript
200 lines
5.2 KiB
TypeScript
import puppeteer, { ConsoleMessage } from 'puppeteer';
|
|
import { PenpotApi } from './api';
|
|
import { getFileUrl } from './get-file-url';
|
|
import { idObjectToArray } from './clean-id';
|
|
import { Shape } from '../models/shape.model';
|
|
|
|
const screenshotsEnable = process.env['E2E_SCREENSHOTS'] === 'true';
|
|
|
|
function replaceIds(shapes: Shape[]) {
|
|
let id = 1;
|
|
|
|
const getId = () => {
|
|
return String(id++);
|
|
};
|
|
|
|
function replaceChildrenId(id: string, newId: string) {
|
|
for (const node of shapes) {
|
|
if (node.parentId === id) {
|
|
node.parentId = newId;
|
|
}
|
|
|
|
if (node.frameId === id) {
|
|
node.frameId = newId;
|
|
}
|
|
|
|
if (node.shapes) {
|
|
node.shapes = node.shapes?.map((shapeId) => {
|
|
return shapeId === id ? newId : shapeId;
|
|
});
|
|
}
|
|
|
|
if (node.layoutGridCells) {
|
|
node.layoutGridCells = idObjectToArray(node.layoutGridCells, newId);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const node of shapes) {
|
|
const previousId = node.id;
|
|
|
|
node.id = getId();
|
|
|
|
replaceChildrenId(previousId, node.id);
|
|
}
|
|
}
|
|
|
|
export async function Agent() {
|
|
console.log('Initializing Penpot API...');
|
|
const penpotApi = await PenpotApi();
|
|
|
|
console.log('Creating file...');
|
|
const file = await penpotApi.createFile();
|
|
console.log('File created with id:', file['~:id']);
|
|
|
|
const fileUrl = getFileUrl(file);
|
|
console.log('File URL:', fileUrl);
|
|
|
|
console.log('Launching browser...');
|
|
const browser = await puppeteer.launch({
|
|
headless: process.env['E2E_HEADLESS'] !== 'false',
|
|
args: ['--ignore-certificate-errors'],
|
|
});
|
|
const page = await browser.newPage();
|
|
|
|
await page.setViewport({ width: 1920, height: 1080 });
|
|
await page.setExtraHTTPHeaders({
|
|
'X-Client': 'plugins/e2e:puppeter',
|
|
});
|
|
|
|
console.log('Setting authentication cookie...');
|
|
page.setCookie({
|
|
name: 'auth-token',
|
|
value: penpotApi.getAuth().split('=')[1],
|
|
domain: 'localhost',
|
|
path: '/',
|
|
expires: (Date.now() + 3600 * 1000) / 1000,
|
|
});
|
|
|
|
console.log('Navigating to file URL...');
|
|
await page.goto(fileUrl);
|
|
await page.waitForSelector('[data-testid="viewport"]');
|
|
console.log('Page loaded and viewport selector found.');
|
|
|
|
page
|
|
.on('console', async (message) => {
|
|
console.log(`${message.type()} ${message.text()}`);
|
|
})
|
|
.on('pageerror', (message) => {
|
|
console.error('Page error:', message);
|
|
});
|
|
|
|
const finish = async () => {
|
|
console.log('Deleting file and closing browser...');
|
|
// TODO
|
|
// await penpotApi.deleteFile(file['~:id']);
|
|
if (process.env['E2E_CLOSE_BROWSER'] !== 'false') {
|
|
await browser.close();
|
|
}
|
|
console.log('Clean up done.');
|
|
};
|
|
|
|
return {
|
|
async runCode(
|
|
code: string,
|
|
options: {
|
|
screenshot?: string;
|
|
autoFinish?: boolean;
|
|
} = {
|
|
screenshot: '',
|
|
autoFinish: true,
|
|
},
|
|
) {
|
|
const autoFinish = options.autoFinish ?? true;
|
|
|
|
console.log('Running plugin code...');
|
|
await page.evaluate((testingPlugin) => {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
(globalThis as any).ɵloadPlugin({
|
|
pluginId: '00000000-0000-0000-0000-000000000000',
|
|
name: 'Test',
|
|
code: `
|
|
(${testingPlugin})();
|
|
`,
|
|
icon: '',
|
|
description: '',
|
|
permissions: [
|
|
'content:read',
|
|
'content:write',
|
|
'library:read',
|
|
'library:write',
|
|
'user:read',
|
|
'comment:read',
|
|
'comment:write',
|
|
'allow:downloads',
|
|
'allow:localstorage',
|
|
],
|
|
});
|
|
}, code);
|
|
|
|
if (options.screenshot && screenshotsEnable) {
|
|
console.log('Taking screenshot:', options.screenshot);
|
|
await page.screenshot({
|
|
path: 'screenshots/' + options.screenshot + '.png',
|
|
});
|
|
}
|
|
|
|
const result = await new Promise((resolve) => {
|
|
const handleConsole = async (msg: ConsoleMessage) => {
|
|
const args = (await Promise.all(
|
|
msg.args().map((arg) => arg.jsonValue()),
|
|
)) as unknown[];
|
|
|
|
const type = args[0];
|
|
const data = args[1];
|
|
|
|
if (type !== 'objects' || !data || typeof data !== 'object') {
|
|
console.log('Invalid console message, waiting for valid one...');
|
|
page.once('console', handleConsole);
|
|
return;
|
|
}
|
|
|
|
const result = Object.values(data) as Shape[];
|
|
|
|
replaceIds(result);
|
|
console.log('IDs replaced in result.');
|
|
|
|
resolve(result);
|
|
};
|
|
|
|
page.once('console', handleConsole);
|
|
|
|
console.log('Evaluating debug.dump_objects...');
|
|
page.evaluate(`
|
|
debug.dump_objects();
|
|
`);
|
|
});
|
|
|
|
await page.waitForNetworkIdle({ idleTime: 2000 });
|
|
|
|
// Wait for the update-file API call to complete
|
|
if (process.env['E2E_WAIT_API_RESPONSE'] === 'true') {
|
|
await page.waitForResponse(
|
|
(response) =>
|
|
response.url().includes('api/main/methods/update-file') &&
|
|
response.status() === 200,
|
|
{ timeout: 10000 },
|
|
);
|
|
}
|
|
|
|
if (autoFinish) {
|
|
console.log('Auto finish enabled. Cleaning up...');
|
|
await finish();
|
|
}
|
|
|
|
return result;
|
|
},
|
|
finish,
|
|
};
|
|
}
|