const longTimeout = 60000; const isProd = process.env.NODE_ENV === 'production'; const baseUrl = isProd ? 'http://localhost:3000/dist/' : 'http://localhost:4000/'; 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)); const testName = expect.getState().currentTestName; console.log(`[${testName}] Messages:`, JSON.stringify(messages.map(({ text }) => text), null, 2)); const errors = messages.filter(msg => msg.type === 'error' && !(msg.text.includes('404') && msg.stack.some(s => s.url.indexOf('fonts/InterVariable.woff') >= 0))); expect(errors).toHaveLength(0); }); function loadSrc(src) { return page.goto(`${baseUrl}#src=${encodeURIComponent(src)}`); } function loadPath(path) { return page.goto(`${baseUrl}#path=${encodeURIComponent(path)}`); } function loadUrl(url) { return page.goto(`${baseUrl}#url=${encodeURIComponent(url)}`); } 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); } 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(() => { // 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; } } } return null; }, { timeout: 45000 }); // Increase timeout to 45 seconds } function waitForLabel(text) { return page.waitForFunction(() => { return Array.from(document.querySelectorAll('label')).find(el => el.textContent === 'myVar'); // return Array.from(document.querySelectorAll('label')).find(el => el.textContent === text); }); } describe('e2e', () => { test('load the default page', async () => { await page.goto(baseUrl); await waitForViewer(); expectObjectList(); }, longTimeout); test('can render cube', async () => { await loadSrc('cube([10, 10, 10]);'); await waitForViewer(); expect3DPolySet(); }, longTimeout); test('use BOSL2', async () => { await loadSrc(` include ; prismoid([40,40], [0,0], h=20); `); await waitForViewer(); expect3DPolySet(); }, longTimeout); test('use NopSCADlib', async () => { await loadSrc(` include meter(led_meter); `); await waitForViewer(); expect3DManifold(); }, longTimeout); test('load a demo by path', async () => { await loadPath('/libraries/closepoints/demo_3D_art.scad'); await waitForViewer(); expect3DPolySet(); }, longTimeout); test('load a demo by url', async () => { await loadUrl('https://github.com/tenstad/keyboard/blob/main/keyboard.scad'); await waitForViewer(); expect3DManifold(); }, longTimeout); test('customizer & windows line endings work', async () => { await loadSrc([ 'myVar = 10;', 'cube(myVar);', ].join('\r\n')); await waitForViewer(); expect3DPolySet(); // 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 }); await (await waitForCustomizeButton()).click(); await page.waitForSelector('fieldset'); await waitForLabel('myVar'); }, longTimeout); });