🐛 Fix e2e tests for plugins

This commit is contained in:
Juanfran
2026-02-13 09:35:56 +01:00
committed by Andrey Antukh
parent bd7f4dca3a
commit 449aa65f8d
12 changed files with 435 additions and 307 deletions

View File

@@ -126,6 +126,6 @@
(defn check-permission
[plugin-id permission]
(or (= plugin-id "TEST")
(or (= plugin-id "00000000-0000-0000-0000-000000000000")
(let [{:keys [permissions]} (dm/get-in @registry [:data plugin-id])]
(contains? permissions permission))))

View File

@@ -18,7 +18,7 @@
(let [;; ==== Setup
store (ths/setup-store (cthf/sample-file :file1 :page-label :page1))
^js context (api/create-context "TEST")
^js context (api/create-context "00000000-0000-0000-0000-000000000000")
_ (set! st/state store)

View File

@@ -28,5 +28,5 @@ export default [
files: ['**/*.js', '**/*.jsx'],
rules: {},
},
{ ignores: ['vite.config.ts'] },
{ ignores: ['vite.config.ts', 'vitest.setup.ts'] },
];

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,7 @@ import comments from './plugins/create-comments';
import { Agent } from './utils/agent';
describe('Plugins', () => {
it.only('create board - text - rectable', async () => {
it('create board - text - rectable', async () => {
const agent = await Agent();
const result = await agent.runCode(testingPlugin.toString(), {
screenshot: 'create-board-text-rect',
@@ -29,6 +29,7 @@ describe('Plugins', () => {
it('create grid layout', async () => {
const agent = await Agent();
const result = await agent.runCode(grid.toString(), {
screenshot: 'create-gridlayout',
});
@@ -83,9 +84,9 @@ describe('Plugins', () => {
it('comments', async () => {
const agent = await Agent();
console.log(comments.toString());
const result = await agent.runCode(comments.toString(), {
screenshot: 'create-comments',
avoidSavedStatus: true,
});
expect(result).toMatchSnapshot();
});

View File

@@ -1,4 +1,4 @@
import puppeteer from 'puppeteer';
import puppeteer, { ConsoleMessage } from 'puppeteer';
import { PenpotApi } from './api';
import { getFileUrl } from './get-file-url';
import { idObjectToArray } from './clean-id';
@@ -56,7 +56,10 @@ export async function Agent() {
console.log('File URL:', fileUrl);
console.log('Launching browser...');
const browser = await puppeteer.launch({args: ['--ignore-certificate-errors']});
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 });
@@ -88,8 +91,11 @@ export async function Agent() {
const finish = async () => {
console.log('Deleting file and closing browser...');
await penpotApi.deleteFile(file['~:id']);
await browser.close();
// TODO
// await penpotApi.deleteFile(file['~:id']);
if (process.env['E2E_CLOSE_BROWSER'] !== 'false') {
await browser.close();
}
console.log('Clean up done.');
};
@@ -99,11 +105,9 @@ export async function Agent() {
options: {
screenshot?: string;
autoFinish?: boolean;
avoidSavedStatus?: boolean;
} = {
screenshot: '',
autoFinish: true,
avoidSavedStatus: false,
},
) {
const autoFinish = options.autoFinish ?? true;
@@ -112,28 +116,27 @@ export async function Agent() {
await page.evaluate((testingPlugin) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).ɵloadPlugin({
pluginId: 'TEST',
pluginId: '00000000-0000-0000-0000-000000000000',
name: 'Test',
code: `
(${testingPlugin})();
`,
icon: '',
description: '',
permissions: ['content:read', 'content:write'],
permissions: [
'content:read',
'content:write',
'library:read',
'library:write',
'user:read',
'comment:read',
'comment:write',
'allow:downloads',
'allow:localstorage',
],
});
}, code);
if (!options.avoidSavedStatus) {
console.log('Waiting for save status...');
await page.waitForSelector(
'.main_ui_workspace_right_header__saved-status',
{
timeout: 10000,
},
);
console.log('Save status found.');
}
if (options.screenshot && screenshotsEnable) {
console.log('Taking screenshot:', options.screenshot);
await page.screenshot({
@@ -141,30 +144,55 @@ export async function Agent() {
});
}
return new Promise((resolve) => {
page.once('console', async (msg) => {
const result = await new Promise((resolve) => {
const handleConsole = async (msg: ConsoleMessage) => {
const args = (await Promise.all(
msg.args().map((arg) => arg.jsonValue()),
)) as Record<string, unknown>[];
)) as unknown[];
const result = Object.values(args[1]) as Shape[];
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);
};
if (autoFinish) {
console.log('Auto finish enabled. Cleaning up...');
finish();
}
});
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,
};

View File

@@ -1,5 +1,4 @@
import { FileRpc } from '../models/file-rpc.model';
const apiUrl = 'https://localhost:3449';
export async function PenpotApi() {
@@ -8,8 +7,8 @@ export async function PenpotApi() {
}
const body = JSON.stringify({
'email': process.env['E2E_LOGIN_EMAIL'],
'password': process.env['E2E_LOGIN_PASSWORD'],
email: process.env['E2E_LOGIN_EMAIL'],
password: process.env['E2E_LOGIN_PASSWORD'],
});
const resultLoginRequest = await fetch(
@@ -18,25 +17,18 @@ export async function PenpotApi() {
credentials: 'include',
method: 'POST',
headers: {
'Content-Type': 'application/json'
'Content-Type': 'application/json',
},
body: body
body: body,
},
);
console.log("AAAAAAAAAAAA", 1, apiUrl)
// console.log("AAAAAAAAAAAA", 2, resultLoginRequest);
console.dir(resultLoginRequest.headers, {depth:20});
console.log('Document Cookies:', window.document.cookie);
const loginData = await resultLoginRequest.json();
const authToken = resultLoginRequest.headers
.get('set-cookie')
?.split(';')
.at(0);
.getSetCookie()
.find((cookie: string) => cookie.startsWith('auth-token='))
?.split(';')[0];
if (!authToken) {
throw new Error('Login failed');
@@ -62,6 +54,9 @@ export async function PenpotApi() {
'fdata/objects-map',
'fdata/pointer-map',
'fdata/shape-data-type',
'fdata/path-data',
'design-tokens/v1',
'variants/v1',
'components/v2',
'styles/v2',
'layout/grid',
@@ -72,7 +67,9 @@ export async function PenpotApi() {
},
);
return (await createFileRequest.json()) as FileRpc;
const fileData = (await createFileRequest.json()) as FileRpc;
console.log('File data received:', fileData);
return fileData;
},
deleteFile: async (fileId: string) => {
const deleteFileRequest = await fetch(

View File

@@ -6,5 +6,5 @@ export function getFileUrl(file: FileRpc) {
const fileId = cleanId(file['~:id']);
const pageId = cleanId(file['~:data']['~:pages'][0]);
return `http://localhost:3449/#/workspace/${projectId}/${fileId}?page-id=${pageId}`;
return `https://localhost:3449/#/workspace/${projectId}/${fileId}?page-id=${pageId}`;
}

View File

@@ -7,13 +7,27 @@ export default defineConfig({
testTimeout: 20000,
watch: false,
globals: true,
environment: 'happy-dom',
environment: 'node',
environmentOptions: {
happyDOM: {
settings: {
disableCSSFileLoading: true,
disableJavaScriptFileLoading: true,
disableJavaScriptEvaluation: true,
enableFileSystemHttpRequests: false,
navigator: {
userAgent:
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
},
},
},
},
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: '../coverage/e2e',
provider: 'v8',
},
setupFiles: ['dotenv/config', 'vitest.setup.ts']
setupFiles: ['dotenv/config', 'vitest.setup.ts'],
},
});

View File

@@ -1,3 +1 @@
// import { vi } from 'vitest';
window.location.href = 'https://localhost:3449';
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';

View File

@@ -9,7 +9,10 @@
```env
E2E_LOGIN_EMAIL="test@penpot.app"
E2E_LOGIN_PASSWORD="123123123"
E2E_SCREENSHOTS= "true"
E2E_SCREENSHOTS="true" # Enable/disable screenshots (default: false)
E2E_HEADLESS="false" # Run browser in headless mode (default: true)
E2E_CLOSE_BROWSER="true" # Close browser after tests (default: true)
E2E_WAIT_API_RESPONSE="false" # Wait for update-file API response (default: false)
```
2. **Run E2E Tests**
@@ -77,5 +80,5 @@
If you need to refresh all the snapshopts run the test with the update option:
```bash
pnpm run test:e2e -- --update
pnpm run test:e2e --update
```

View File

@@ -28,7 +28,9 @@ export const initPluginsRuntime = (contextBuilder: (id: string) => Context) => {
try {
console.log('%c[PLUGINS] Initialize runtime', 'color: #008d7c');
setContextBuilder(contextBuilder);
globalThisAny$.ɵcontext = contextBuilder('TEST');
globalThisAny$.ɵcontext = contextBuilder(
'00000000-0000-0000-0000-000000000000',
);
globalThis.ɵloadPlugin = ɵloadPlugin;
globalThis.ɵloadPluginByUrl = ɵloadPluginByUrl;
globalThis.ɵunloadPlugin = ɵunloadPlugin;