2024-12-22 22:41:35 +00:00
|
|
|
const longTimeout = 60000;
|
|
|
|
|
|
|
|
|
|
const isProd = process.env.NODE_ENV === 'production';
|
2024-12-26 19:18:46 +00:00
|
|
|
const baseUrl = isProd ? 'http://localhost:3000/dist/' : 'http://localhost:4000/';
|
2024-12-22 22:41:35 +00:00
|
|
|
|
2024-12-25 11:20:55 +00:00
|
|
|
const messages = [];
|
|
|
|
|
|
|
|
|
|
beforeAll(async () => {
|
|
|
|
|
page.on('console', (msg) => messages.push({
|
|
|
|
|
type: msg.type(),
|
|
|
|
|
text: msg.text(),
|
|
|
|
|
stack: msg.stackTrace(),
|
|
|
|
|
location: msg.location(),
|
|
|
|
|
}));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
messages.length = 0;
|
|
|
|
|
await page.goto('about:blank');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
// console.log('Messages:', JSON.stringify(messages, null, 2));
|
2024-12-25 13:58:44 +00:00
|
|
|
const testName = expect.getState().currentTestName;
|
2025-08-16 18:21:01 +02:00
|
|
|
console.log(`[${testName}] Messages:`, JSON.stringify(messages.map(({ text }) => text), null, 2));
|
|
|
|
|
|
2024-12-25 11:20:55 +00:00
|
|
|
const errors = messages.filter(msg =>
|
2025-08-16 18:21:01 +02:00
|
|
|
msg.type === 'error' &&
|
|
|
|
|
!(msg.text.includes('404')
|
|
|
|
|
&& msg.stack.some(s =>
|
|
|
|
|
s.url.indexOf('fonts/InterVariable.woff') >= 0)));
|
2024-12-25 11:20:55 +00:00
|
|
|
expect(errors).toHaveLength(0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function loadSrc(src) {
|
2025-08-16 18:21:01 +02:00
|
|
|
return page.goto(`${baseUrl}#src=${encodeURIComponent(src)}`);
|
2024-12-25 11:20:55 +00:00
|
|
|
}
|
2024-12-25 13:58:44 +00:00
|
|
|
function loadPath(path) {
|
2025-08-16 18:21:01 +02:00
|
|
|
return page.goto(`${baseUrl}#path=${encodeURIComponent(path)}`);
|
2024-12-26 19:18:46 +00:00
|
|
|
}
|
|
|
|
|
function loadUrl(url) {
|
2025-08-16 18:21:01 +02:00
|
|
|
return page.goto(`${baseUrl}#url=${encodeURIComponent(url)}`);
|
2024-12-25 13:58:44 +00:00
|
|
|
}
|
2024-12-25 11:20:55 +00:00
|
|
|
async function waitForViewer() {
|
|
|
|
|
await page.waitForSelector('model-viewer');
|
|
|
|
|
await page.waitForFunction(() => {
|
|
|
|
|
const viewer = document.querySelector('model-viewer.main-viewer');
|
|
|
|
|
return viewer && viewer.src !== '';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
function expectMessage(messages, line) {
|
|
|
|
|
const successMessage = messages.filter(msg => msg.type === 'debug' && msg.text === line);
|
|
|
|
|
expect(successMessage).toHaveLength(1);
|
|
|
|
|
}
|
2024-12-25 12:00:39 +00:00
|
|
|
function expectObjectList() {
|
|
|
|
|
expectMessage(messages, 'stderr: Top level object is a list of objects:');
|
|
|
|
|
}
|
|
|
|
|
function expect3DPolySet() {
|
|
|
|
|
expectMessage(messages, 'stderr: Top level object is a 3D object (PolySet):');
|
|
|
|
|
}
|
|
|
|
|
function expect3DManifold() {
|
|
|
|
|
expectMessage(messages, 'stderr: Top level object is a 3D object (manifold):');
|
|
|
|
|
}
|
|
|
|
|
function waitForCustomizeButton() {
|
|
|
|
|
return page.waitForFunction(() => {
|
2025-08-16 18:21:01 +02:00
|
|
|
// Try multiple selectors for PrimeReact components
|
|
|
|
|
// ToggleButton might render as button or input elements
|
|
|
|
|
const selectors = [
|
|
|
|
|
'input[role=switch]',
|
|
|
|
|
'button',
|
|
|
|
|
'[role=tab]',
|
|
|
|
|
'.p-togglebutton',
|
|
|
|
|
'.p-tabmenu-nav a'
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
for (const selector of selectors) {
|
|
|
|
|
const elements = document.querySelectorAll(selector);
|
|
|
|
|
for (const element of elements) {
|
|
|
|
|
const text = element.textContent || element.innerText || '';
|
|
|
|
|
const parentText = element.parentElement?.textContent || element.parentElement?.innerText || '';
|
|
|
|
|
if (text.includes('Customize') || parentText.includes('Customize')) {
|
|
|
|
|
return element;
|
|
|
|
|
}
|
2024-12-25 12:00:39 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-08-16 18:21:01 +02:00
|
|
|
return null;
|
|
|
|
|
}, { timeout: 45000 }); // Increase timeout to 45 seconds
|
2024-12-25 12:00:39 +00:00
|
|
|
}
|
|
|
|
|
function waitForLabel(text) {
|
2025-08-16 18:21:01 +02:00
|
|
|
return page.waitForFunction(() => {
|
2024-12-25 12:00:39 +00:00
|
|
|
return Array.from(document.querySelectorAll('label')).find(el => el.textContent === 'myVar');
|
|
|
|
|
// return Array.from(document.querySelectorAll('label')).find(el => el.textContent === text);
|
2025-08-16 18:21:01 +02:00
|
|
|
});
|
2024-12-25 12:00:39 +00:00
|
|
|
}
|
2024-12-25 11:20:55 +00:00
|
|
|
|
|
|
|
|
describe('e2e', () => {
|
|
|
|
|
test('load the default page', async () => {
|
2024-12-26 19:18:46 +00:00
|
|
|
await page.goto(baseUrl);
|
2024-12-25 11:20:55 +00:00
|
|
|
await waitForViewer();
|
2024-12-25 12:04:44 +00:00
|
|
|
expectObjectList();
|
2024-12-25 11:20:55 +00:00
|
|
|
}, longTimeout);
|
|
|
|
|
|
|
|
|
|
test('can render cube', async () => {
|
|
|
|
|
await loadSrc('cube([10, 10, 10]);');
|
|
|
|
|
await waitForViewer();
|
2024-12-25 12:00:39 +00:00
|
|
|
expect3DPolySet();
|
2024-12-25 11:20:55 +00:00
|
|
|
}, longTimeout);
|
2025-08-16 18:21:01 +02:00
|
|
|
|
2024-12-25 11:20:55 +00:00
|
|
|
test('use BOSL2', async () => {
|
|
|
|
|
await loadSrc(`
|
|
|
|
|
include <BOSL2/std.scad>;
|
|
|
|
|
prismoid([40,40], [0,0], h=20);
|
|
|
|
|
`);
|
|
|
|
|
await waitForViewer();
|
2024-12-25 12:13:12 +00:00
|
|
|
expect3DPolySet();
|
2024-12-25 11:20:55 +00:00
|
|
|
}, longTimeout);
|
|
|
|
|
|
|
|
|
|
test('use NopSCADlib', async () => {
|
|
|
|
|
await loadSrc(`
|
|
|
|
|
include <NopSCADlib/vitamins/led_meters.scad>
|
|
|
|
|
meter(led_meter);
|
|
|
|
|
`);
|
|
|
|
|
await waitForViewer();
|
2024-12-25 12:00:39 +00:00
|
|
|
expect3DManifold();
|
|
|
|
|
}, longTimeout);
|
|
|
|
|
|
2024-12-25 13:58:44 +00:00
|
|
|
test('load a demo by path', async () => {
|
|
|
|
|
await loadPath('/libraries/closepoints/demo_3D_art.scad');
|
|
|
|
|
await waitForViewer();
|
|
|
|
|
expect3DPolySet();
|
|
|
|
|
}, longTimeout);
|
|
|
|
|
|
2024-12-26 19:18:46 +00:00
|
|
|
test('load a demo by url', async () => {
|
|
|
|
|
await loadUrl('https://github.com/tenstad/keyboard/blob/main/keyboard.scad');
|
|
|
|
|
await waitForViewer();
|
|
|
|
|
expect3DManifold();
|
|
|
|
|
}, longTimeout);
|
|
|
|
|
|
2024-12-25 12:00:39 +00:00
|
|
|
test('customizer & windows line endings work', async () => {
|
|
|
|
|
await loadSrc([
|
|
|
|
|
'myVar = 10;',
|
|
|
|
|
'cube(myVar);',
|
|
|
|
|
].join('\r\n'));
|
|
|
|
|
await waitForViewer();
|
|
|
|
|
expect3DPolySet();
|
2025-08-16 18:21:01 +02:00
|
|
|
|
|
|
|
|
// Wait for syntax checking to complete and parameters to be detected
|
|
|
|
|
await page.waitForFunction(() => {
|
|
|
|
|
// Look for any indication that parameters have been processed
|
|
|
|
|
const messages = Array.from(document.querySelectorAll('*')).map(el => el.textContent || '').join(' ');
|
|
|
|
|
return messages.includes('myVar') || messages.includes('Customize');
|
|
|
|
|
}, { timeout: 30000 });
|
|
|
|
|
|
2024-12-25 12:00:39 +00:00
|
|
|
await (await waitForCustomizeButton()).click();
|
|
|
|
|
await page.waitForSelector('fieldset');
|
|
|
|
|
await waitForLabel('myVar');
|
2024-12-22 22:41:35 +00:00
|
|
|
}, longTimeout);
|
2024-12-25 11:20:55 +00:00
|
|
|
});
|
2025-08-16 18:21:01 +02:00
|
|
|
|