import { test, expect } from '@playwright/test'; interface InteractiveFile { contents: string; ext: string; name: string; contentsHtml: string; } interface Nodule { type: 'paragraph' | 'interactiveEditor'; data: string | InteractiveFile[]; } interface PageData { result: { data: { challengeNode: { challenge: { title: string; nodules: Nodule[]; }; }; }; }; } const challengePath = '/learn/responsive-web-design-v9/lecture-what-is-css/what-are-some-default-browser-styles-applied-to-html'; const challengeTitle = 'Test Challenge Title'; test.describe('Interactive Editor', () => { test('should render paragraph nodules as text and not show the interactive editor toggle', async ({ page }) => { await page.route( `**/page-data${challengePath}/page-data.json`, async route => { const response = await route.fetch(); const body = await response.text(); const pageData = JSON.parse(body) as PageData; pageData.result.data.challengeNode.challenge.title = challengeTitle; pageData.result.data.challengeNode.challenge.nodules = [ { type: 'paragraph', data: '

This is a plain text paragraph.

' }, { type: 'paragraph', data: '

Another paragraph with code in it.

' } ]; await route.fulfill({ contentType: 'application/json', body: JSON.stringify(pageData) }); } ); await page.goto(challengePath); await expect( page.getByRole('heading', { name: challengeTitle }) ).toBeVisible(); await expect( page.getByText('This is a plain text paragraph.') ).toBeVisible(); await expect( page.getByText('Another paragraph with code in it.') ).toBeVisible(); await expect( page.getByRole('button', { name: /interactive editor/i }) ).not.toBeVisible(); }); test('should toggle between interactive editor and static code view when Interactive Editor button is clicked', async ({ page }) => { await page.route( `**/page-data${challengePath}/page-data.json`, async route => { const response = await route.fetch(); const body = await response.text(); const pageData = JSON.parse(body) as PageData; pageData.result.data.challengeNode.challenge.title = challengeTitle; pageData.result.data.challengeNode.challenge.nodules = [ { type: 'paragraph', data: '

Introduction paragraph.

' }, { type: 'interactiveEditor', data: [ { contents: 'console.log("Toggle test");', ext: 'js', name: 'script-1', contentsHtml: '
console.log("Toggle test");
' }, { contents: '
HTML content
', ext: 'html', name: 'index-1', contentsHtml: '
<div>HTML content</div>
' } ] }, { type: 'paragraph', data: '

Final paragraph.

' } ]; await route.fulfill({ contentType: 'application/json', body: JSON.stringify(pageData) }); } ); await page.goto(challengePath); await expect( page.getByRole('heading', { name: challengeTitle }) ).toBeVisible(); await expect(page.getByText('Introduction paragraph.')).toBeVisible(); await expect(page.getByText('Final paragraph.')).toBeVisible(); // Initially, interactive editor should be hidden, static code view should be visible await expect(page.getByTestId('sp-interactive-editor')).not.toBeVisible(); await expect( page .locator('pre code') .filter({ hasText: 'console.log("Toggle test");' }) ).toHaveCount(1); await expect( page.locator('pre code').filter({ hasText: '
HTML content
' }) ).toHaveCount(1); await expect( page.evaluate(() => localStorage.getItem('showInteractiveEditor')) ).resolves.toBe(null); // Click the toggle button const toggleButton = page.getByRole('button', { name: /interactive editor/i }); await toggleButton.click(); // Interactive editor should be visible, static code view hidden await expect(page.getByTestId('sp-interactive-editor')).toBeVisible(); await expect(page.locator('pre code')).not.toBeVisible(); await expect( page.evaluate(() => localStorage.getItem('showInteractiveEditor')) ).resolves.toBe('true'); // Click the toggle button again await toggleButton.click(); // Interactive editor should be hidden, static code view visible again await expect(page.getByTestId('sp-interactive-editor')).not.toBeVisible(); await expect( page .locator('pre code') .filter({ hasText: 'console.log("Toggle test");' }) ).toBeVisible(); await expect( page.locator('pre code').filter({ hasText: '
HTML content
' }) ).toBeVisible(); await expect( page.evaluate(() => localStorage.getItem('showInteractiveEditor')) ).resolves.toBe('false'); }); test('should hide console panel in JS-only interactive editor to prevent output duplication', async ({ page }) => { await page.route( `**/page-data${challengePath}/page-data.json`, async route => { const response = await route.fetch(); const body = await response.text(); const pageData = JSON.parse(body) as PageData; pageData.result.data.challengeNode.challenge.title = challengeTitle; pageData.result.data.challengeNode.challenge.nodules = [ { type: 'paragraph', data: '

This challenge has only JavaScript code.

' }, { type: 'interactiveEditor', data: [ { contents: 'console.log("Hello from JS-only editor");', ext: 'js', name: 'script-1', contentsHtml: '
console.log("Hello from JS-only editor");
' } ] } ]; await route.fulfill({ contentType: 'application/json', body: JSON.stringify(pageData) }); } ); await page.goto(challengePath); await expect( page.getByRole('heading', { name: challengeTitle }) ).toBeVisible(); await expect( page.getByText('This challenge has only JavaScript code.') ).toBeVisible(); await expect( page .locator('pre code') .filter({ hasText: 'console.log("Hello from JS-only editor");' }) ).toBeVisible(); // Click the toggle button to show interactive editor await page .getByRole('button', { name: /interactive editor/i }) .click(); // Check that the console is visible and the console wrapper is hidden await expect(page.locator('.sp-console')).toBeVisible(); await expect(page.locator('.sp-console-wrapper')).not.toBeVisible(); }); });