diff --git a/.changeset/cuddly-pandas-peel.md b/.changeset/cuddly-pandas-peel.md new file mode 100644 index 00000000..a20eab45 --- /dev/null +++ b/.changeset/cuddly-pandas-peel.md @@ -0,0 +1,5 @@ +--- +'@solid-devtools/debugger': patch +--- + +Fix inspecting context nodes after Solid 1.8. (fixes #272) diff --git a/.changeset/purple-melons-trade.md b/.changeset/purple-melons-trade.md new file mode 100644 index 00000000..83e2fb9d --- /dev/null +++ b/.changeset/purple-melons-trade.md @@ -0,0 +1,10 @@ +--- +'@solid-devtools/debugger': patch +'@solid-devtools/frontend': patch +'@solid-devtools/overlay': patch +'@solid-devtools/logger': patch +'@solid-devtools/shared': patch +'solid-devtools': patch +--- + +Correct solid-js peer dep version diff --git a/e2e/fixtures.ts b/e2e/fixtures.ts index c02b5e12..4d35715a 100644 --- a/e2e/fixtures.ts +++ b/e2e/fixtures.ts @@ -1,37 +1,40 @@ -import { FrameLocator, Locator, Page, test as base, chromium } from '@playwright/test' +import * as pw from '@playwright/test' import assert from 'assert' import path from 'path' -export const test = base.extend<{ - page: Page - sdtFrame: FrameLocator | Locator - search: Locator +export const test = pw.test.extend<{ + page: pw.Page + sdt_frame: pw.FrameLocator | pw.Locator + search: pw.Locator }>({ + baseURL: async ({}, use, testInfo) => { + await use(testInfo.project.use.baseURL) + }, context: async ({ baseURL }, use, testInfo) => { if (testInfo.project.name.includes('Overlay')) { - const context = await chromium.launchPersistentContext('', { baseURL }) + const context = await pw.chromium.launchPersistentContext('', { baseURL: baseURL }) await use(context) await context.close() return } - const pathToExtension = path.resolve(__dirname, '../packages/extension/dist/') - const context = await chromium.launchPersistentContext('', { + const path_to_extension = path.resolve(__dirname, '../packages/extension/dist/') + const context = await pw.chromium.launchPersistentContext('', { args: [ '--headless=new', - `--disable-extensions-except=${pathToExtension}`, - `--load-extension=${pathToExtension}`, + `--disable-extensions-except=${path_to_extension}`, + `--load-extension=${path_to_extension}`, ], devtools: true, // will open devtools when new tab is opened - baseURL, + baseURL: baseURL, }) const page = context.pages().find(page => page.url().includes('about:blank')) assert(page) - const devtoolsPanel = context + const devtools_panel = context .pages() .find(page => page.url().includes('devtools://devtools/bundled/devtools_app.html')) - assert(devtoolsPanel) + assert(devtools_panel) // Since we can only initiate DevTools by opening a new tab, we need to go to // the sandbox in a new tab by clicking a link. @@ -43,22 +46,19 @@ export const test = base.extend<{ document.body.appendChild(a) }, baseURL!) - const pagePromise = context.waitForEvent('page') + const page_promise = context.waitForEvent('page') await page.getByText('open new tab').click() - const newPage = await pagePromise + const new_page = await page_promise // wait for the new page to be interactive - await newPage.getByRole('button').first().click({ trial: true }) + await new_page.getByRole('button').first().click({ trial: true }) - await devtoolsPanel.close() + await devtools_panel.close() await page.close() await use(context) await context.close() }, - baseURL: async ({}, use, testInfo) => { - await use(testInfo.project.use.baseURL) - }, page: async ({ context, baseURL }, use, testInfo) => { if (testInfo.project.name.includes('Overlay')) { const page = context.pages()[0]! @@ -71,34 +71,36 @@ export const test = base.extend<{ assert(page) await use(page) }, - sdtFrame: async ({ context, page }, use, testInfo) => { - let sdtFrame: FrameLocator | Locator + sdt_frame: async ({ context, page }, use, testInfo) => { + let sdt_frame: pw.FrameLocator | pw.Locator if (testInfo.project.name.includes('Overlay')) { - sdtFrame = page.getByTestId('solid-devtools-overlay') + sdt_frame = page.getByTestId('solid-devtools-overlay') } else { - const devtoolsPanel = context + const devtools_panel = context .pages() .find(page => page.url().includes('devtools://devtools/bundled/devtools_app.html')) - assert(devtoolsPanel) + assert(devtools_panel) // Undock the devtools into a separate window so we can see the `solid` tab. - await devtoolsPanel.getByLabel('Customize and control DevTools').click() - await devtoolsPanel.getByLabel('Undock into separate window').click() - await devtoolsPanel.getByText('Solid').click() + await devtools_panel.getByLabel('Customize and control DevTools').click() + await devtools_panel.getByLabel('Undock into separate window').click() + await devtools_panel.getByText('Solid').click() // Somehow the window is not sized properly, causes half of the viewport // to be hidden. Interacting with elements outside the window area // wouldn't work. - await devtoolsPanel.setViewportSize({ width: 640, height: 660 }) + await devtools_panel.setViewportSize({ width: 640, height: 660 }) - sdtFrame = devtoolsPanel.frameLocator('[src^="chrome-extension://"][src$="index.html"]') + sdt_frame = devtools_panel.frameLocator( + '[src^="chrome-extension://"][src$="index.html"]', + ) } - await sdtFrame.getByText('Root').first().waitFor() // Wait for all tree nodes to be visible - await use(sdtFrame) + await sdt_frame.getByText('Root').first().waitFor() // Wait for all tree nodes to be visible + await use(sdt_frame) }, - search: async ({ sdtFrame }, use) => { - await use(sdtFrame.getByPlaceholder('Search')) + search: async ({ sdt_frame }, use) => { + await use(sdt_frame.getByPlaceholder('Search')) }, }) diff --git a/e2e/node-tree.spec.ts b/e2e/node-tree.spec.ts deleted file mode 100644 index e566206d..00000000 --- a/e2e/node-tree.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { expect } from '@playwright/test' -import { test } from './fixtures' - -test('Search node in tree view', async ({ sdtFrame, search }) => { - const selectedNode = sdtFrame.getByRole('treeitem', { selected: true }) - await search.fill('Theme') - - // Press enter to select the matching node - await search.press('Enter') - await expect(selectedNode).toHaveText(/ThemeProvider/) - - // Press enter to go to the next matching node - await search.press('Enter') - await expect(selectedNode).toHaveText(/ThemeExample/) -}) - -test('Picking node using locator', async ({ sdtFrame, page }) => { - const selectedNode = sdtFrame.getByRole('treeitem', { selected: true }) - const zeroIsEven = page.getByText('0 is even!') - - await sdtFrame.getByTitle('Select an element in the page to inspect it').click() - - await zeroIsEven.hover() - await zeroIsEven.click() - - await expect(selectedNode).toHaveText(/Bold/) -}) - -test('Reflected signal in Inspector', async ({ sdtFrame, page }) => { - await sdtFrame.getByText('App').click() - - const count = sdtFrame.getByLabel('count', { exact: true }) - const countButton = page.getByText('Count: 0').first() - - await expect(count).toHaveText(/0/) - await countButton.click() - await expect(count).toHaveText(/1/) -}) - -test('Reflected memos in Inspector', async ({ sdtFrame, page, search }) => { - const todoInput = page.getByPlaceholder('enter todo and click +') - const todoButton = page.getByText('+') - const newTitle = sdtFrame.getByLabel('newTitle', { exact: true }) - const signal = sdtFrame.getByLabel('valuesInASignal signal') - const values = signal.getByLabel('values', { exact: true }) - const title = signal.getByLabel('title', { exact: true }) - const done = signal.getByLabel('done', { exact: true }) - - await search.fill('Todos') - await search.press('Enter') - - await signal.getByLabel(/Expand or collapse/).click() - await expect(values).toHaveText(/Empty Array/) - await expect(newTitle).toBeEmpty() - - await todoInput.fill('Buy groceries') - await expect(newTitle).toHaveText(/Buy groceries/) - await todoButton.click() - await expect(newTitle).toBeEmpty() - - await expect(values).toHaveText('Array [1]') - - await signal.getByLabel('Expand or collapse values', { exact: true }).click() - await signal.getByLabel('Expand or collapse 0').click() - - await expect(title).toHaveText(/Buy groceries/) - await expect(done).toBeChecked({ checked: false }) - await page - .getByRole('checkbox') - .and(page.getByLabel(/Buy groceries/)) - .check() - await expect(done).toBeChecked() -}) diff --git a/e2e/tree-view.test.ts b/e2e/tree-view.test.ts new file mode 100644 index 00000000..c1421f81 --- /dev/null +++ b/e2e/tree-view.test.ts @@ -0,0 +1,103 @@ +import { expect } from '@playwright/test' +import { test } from './fixtures' + +test('Search node in tree view', async ({ sdt_frame, search }) => { + const selectedNode = sdt_frame.getByRole('treeitem', { selected: true }) + await search.fill('Theme') + + // Press enter to select the matching node + await search.press('Enter') + await expect(selectedNode).toHaveText(/ThemeProvider/) + + // Press enter to go to the next matching node + await search.press('Enter') + await expect(selectedNode).toHaveText(/ThemeExample/) +}) + +test('Picking node using locator', async ({ sdt_frame, page }) => { + const selectedNode = sdt_frame.getByRole('treeitem', { selected: true }) + const zeroIsEven = page.getByText('0 is even!') + + await sdt_frame.getByTitle('Select an element in the page to inspect it').click() + + await zeroIsEven.hover() + await zeroIsEven.click() + + await expect(selectedNode).toHaveText(/Bold/) +}) + +test('Reflected signal in Inspector', async ({ sdt_frame, page }) => { + await sdt_frame.getByText('App').click() + + const count = sdt_frame.getByLabel('count', { exact: true }) + const count_button = page.getByText('Count: 0').first() + + await expect(count).toHaveText(/0/) + await count_button.click() + await expect(count).toHaveText(/1/) +}) + +test('Reflected memos in Inspector', async ({ sdt_frame, page, search }) => { + const todo_input = page.getByPlaceholder('enter todo and click +') + const todo_button = page.getByText('+') + const new_title = sdt_frame.getByLabel('newTitle', { exact: true }) + const signal = sdt_frame.getByLabel('valuesInASignal signal') + const values = signal.getByLabel('values', { exact: true }) + const title = signal.getByLabel('title', { exact: true }) + const done = signal.getByLabel('done', { exact: true }) + + await search.fill('Todos') + await search.press('Enter') + + await signal.getByLabel(/Expand or collapse/).click() + await expect(values).toHaveText(/Empty Array/) + await expect(new_title).toBeEmpty() + + await todo_input.fill('Buy groceries') + await expect(new_title).toHaveText(/Buy groceries/) + await todo_button.click() + await expect(new_title).toBeEmpty() + + await expect(values).toHaveText('Array [1]') + + await signal.getByLabel('Expand or collapse values', { exact: true }).click() + await signal.getByLabel('Expand or collapse 0').click() + + await expect(title).toHaveText(/Buy groceries/) + await expect(done).toBeChecked({ checked: false }) + await page + .getByRole('checkbox') + .and(page.getByLabel(/Buy groceries/)) + .check() + await expect(done).toBeChecked() +}) + +test('Inspect context nodes', async ({ sdt_frame, search }) => { + /* + First ctx node is visible immediately + */ + let node = await sdt_frame.getByText('Context').nth(0) + await node.click() + let signal = sdt_frame.getByLabel('value signal', { exact: true }) + let value = signal.getByLabel('value', { exact: true }) + await expect(value).toHaveText('Array [2]') + + /* + Second ctx node can be below the fold + It's the one above the "PassChildren" node + */ + await search.fill('PassChildren') + await search.press('Enter') + + const tree_items = await sdt_frame.getByRole('treeitem') + const idx = await tree_items.evaluateAll( + els => els.findIndex(el => el.textContent?.includes('PassChildren')) - 1, + ) + node = await tree_items.nth(idx) + + await node.click() + signal = sdt_frame.getByLabel('value signal', { exact: true }) + await signal.waitFor() + value = signal.getByLabel('value', { exact: true }) + await expect(value).toHaveText('Object [5]') +}) diff --git a/packages/debugger/package.json b/packages/debugger/package.json index 00cc266e..29ffbc02 100644 --- a/packages/debugger/package.json +++ b/packages/debugger/package.json @@ -85,7 +85,7 @@ "@solid-primitives/utils": "^6.2.1" }, "peerDependencies": { - "solid-js": "^1.7.0" + "solid-js": "^1.8.0" }, "devDependencies": { "solid-js": "^1.8.5" diff --git a/packages/debugger/src/inspector/inspector.ts b/packages/debugger/src/inspector/inspector.ts index 34eb599f..83b96e5a 100644 --- a/packages/debugger/src/inspector/inspector.ts +++ b/packages/debugger/src/inspector/inspector.ts @@ -290,12 +290,12 @@ export const collectOwnerDetails = /*#__PURE__*/ untrackedCallback(function ( sourceMap = undefined owned = null const symbols = Object.getOwnPropertySymbols(owner.context) - if (symbols.length !== 1) { - throw new Error('Context field has more than one symbol. This is not expected.') - } else { - const contextValue = owner.context[symbols[0]!] - getValue = () => contextValue - } + /* + since 1.8 context keys from parent are cloned to child context + the last key should be the added value + */ + const context_value = owner.context[symbols[symbols.length - 1]!] + getValue = () => context_value } let checkProxyProps: ReturnType['checkProxyProps'] diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 366933d2..53bf14e9 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -65,7 +65,7 @@ "solid-js": "^1.8.5" }, "peerDependencies": { - "solid-js": "^1.7.0" + "solid-js": "^1.8.0" }, "packageManager": "pnpm@8.6.0" } diff --git a/packages/frontend/src/ui/styles.tsx b/packages/frontend/src/ui/styles.tsx index 9d45e6fc..494dc71d 100644 --- a/packages/frontend/src/ui/styles.tsx +++ b/packages/frontend/src/ui/styles.tsx @@ -19,7 +19,7 @@ export const tag_brackets = 'tag_brackets' export const tag_brackets_styles = /*css*/ ` .${tag_brackets}:before { - content: '<'; + content: '\<'; color: ${theme.vars.disabled}; } .${tag_brackets}:after { diff --git a/packages/logger/package.json b/packages/logger/package.json index 93f259a7..5b079535 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -58,7 +58,7 @@ "@solid-primitives/utils": "^6.2.1" }, "peerDependencies": { - "solid-js": "^1.7.0" + "solid-js": "^1.8.0" }, "packageManager": "pnpm@8.9.0", "keywords": [ diff --git a/packages/main/package.json b/packages/main/package.json index 30aec4e9..8c81b293 100644 --- a/packages/main/package.json +++ b/packages/main/package.json @@ -117,7 +117,7 @@ "@solid-devtools/shared": "workspace:^" }, "peerDependencies": { - "solid-js": "^1.7.0", + "solid-js": "^1.8.0", "solid-start": "^0.2.20", "vite": "^2.2.3 || ^3.0.0 || ^4.0.0" }, diff --git a/packages/overlay/package.json b/packages/overlay/package.json index 85635da4..ff31fa41 100644 --- a/packages/overlay/package.json +++ b/packages/overlay/package.json @@ -56,7 +56,7 @@ "@solid-primitives/utils": "^6.2.1" }, "peerDependencies": { - "solid-js": "^1.7.0" + "solid-js": "^1.8.0" }, "packageManager": "pnpm@8.9.0", "devDependencies": { diff --git a/packages/shared/package.json b/packages/shared/package.json index c3c33024..c23c91e0 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -68,7 +68,7 @@ } }, "peerDependencies": { - "solid-js": "^1.7.0" + "solid-js": "^1.8.0" }, "dependencies": { "@solid-primitives/event-bus": "^1.0.8", diff --git a/playwright.config.ts b/playwright.config.ts index ef07fc93..20c6f64f 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -23,11 +23,13 @@ export default defineConfig({ command: 'pnpm run sandbox:ext', url: 'http://localhost:3000', timeout: 120_000, + reuseExistingServer: !is_ci, }, { command: 'pnpm run sandbox', url: 'http://localhost:3001', timeout: 120_000, + reuseExistingServer: !is_ci, }, ], use: {