File size: 6,623 Bytes
b91e262
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
import { FileRef } from 'e2e-utils'
import { Playwright } from 'next-webdriver'
import { nextTestSetup } from 'e2e-utils'
import { join } from 'path'
import { waitForDevToolsIndicator, retry } from 'next-test-utils'

describe('client-dev-overlay', () => {
  const { next, isTurbopack } = nextTestSetup({
    files: {
      pages: new FileRef(join(__dirname, 'pages')),
    },
    env: {
      // Disable the cooldown period for the dev indicator so that hiding the indicator in a test doesn't
      // impact subsequent tests.
      __NEXT_DEV_INDICATOR_COOLDOWN_MS: '0',
    },
  })

  // The `Playwright.hasElementByCssSelector` cannot be used for elements inside a shadow DOM.
  function elementExistsInNextJSPortalShadowDOM(
    browser: Playwright,
    selector: string
  ) {
    return browser.eval(
      `!!document.querySelector('nextjs-portal').shadowRoot.querySelector('${selector}')`
    ) as any
  }
  const selectors = {
    fullScreenDialog: '[data-nextjs-dialog]',
    toast: '[data-nextjs-toast]',
    popover: '[data-nextjs-dev-tools-button]',
    indicator: '[data-next-badge-root]',
    minimizeButton: 'body',
    preferencesButton: '[data-preferences]',
    hideButton: '[data-hide-dev-tools]',
  }
  function getToast(browser: Playwright) {
    return browser.elementByCss(selectors.toast)
  }
  function getPopover(browser: Playwright) {
    return browser.elementByCss(selectors.popover)
  }
  function getMinimizeButton(browser: Playwright) {
    return browser.elementByCss(selectors.minimizeButton)
  }
  function getHideButton(browser: Playwright) {
    return browser.elementByCss(selectors.hideButton)
  }
  function getPreferencesButton(browser: Playwright) {
    return browser.elementByCss(selectors.preferencesButton)
  }

  it('should be able to fullscreen the minimized overlay', async () => {
    const browser = await next.browser('/')
    await getMinimizeButton(browser).click()
    await getToast(browser).click()

    await retry(async () => {
      expect(
        await elementExistsInNextJSPortalShadowDOM(
          browser,
          selectors.fullScreenDialog
        )
      ).toBe(true)
    })
  })

  it('should be able to minimize the fullscreen overlay', async () => {
    const browser = await next.browser('/')
    await getMinimizeButton(browser).click()
    expect(
      await elementExistsInNextJSPortalShadowDOM(browser, selectors.toast)
    ).toBe(true)
  })

  it('should keep the error indicator visible when there are errors', async () => {
    const browser = await next.browser('/')
    await getMinimizeButton(browser).click()
    await getPopover(browser).click()
    await getPreferencesButton(browser).click()
    await getHideButton(browser).click()

    await retry(async () => {
      const display = await browser.eval(
        `getComputedStyle(document.querySelector('nextjs-portal').shadowRoot.querySelector('${selectors.indicator}')).display`
      )
      expect(display).toBe('block')
    })
  })

  it('should be possible to hide the minimized overlay when there are no errors', async () => {
    const browser = await next.browser('/')
    const originalContent = await next.readFile('pages/index.js')
    try {
      await next.patchFile('pages/index.js', (content) => {
        return content.replace(`throw Error('example runtime error')`, '')
      })

      await getMinimizeButton(browser).click()
      await getPopover(browser).click()
      await getPreferencesButton(browser).click()
      await getHideButton(browser).click()

      await retry(async () => {
        const display = await browser.eval(
          `getComputedStyle(document.querySelector('nextjs-portal').shadowRoot.querySelector('${selectors.indicator}')).display`
        )
        expect(display).toBe('none')
      })
    } finally {
      await next.patchFile('pages/index.js', originalContent)
    }
  })

  it('should have a role of "dialog" if the page is focused', async () => {
    const browser = await next.browser('/')
    await retry(async () => {
      expect(
        await elementExistsInNextJSPortalShadowDOM(browser, '[role="dialog"]')
      ).toBe(true)
    })
  })

  it('should nudge to use Turbopack unless Turbopack is disabled', async () => {
    const browser = await next.browser('/')

    // Don't use toggleDevToolsIndicatorPopover because this is asserting something in the old dev tools menu which isn't preset yet in the new UI.
    const devToolsIndicator = await waitForDevToolsIndicator(browser)
    try {
      await devToolsIndicator.click()
    } catch (cause) {
      const error = new Error('No DevTools Indicator to open.', { cause })
      throw error
    }

    const devtoolsMenu = await browser.elementByCss('#nextjs-dev-tools-menu')
    if (isTurbopack) {
      expect(await devtoolsMenu.innerText()).toMatchInlineSnapshot(`
       "Issues
       1
       Route
       Static
       Bundler
       Turbopack
       Preferences"
      `)
    } else {
      expect(await devtoolsMenu.innerText()).toMatchInlineSnapshot(`
       "Issues
       1
       Route
       Static
       Bundler
       Webpack
       Preferences"
      `)
    }
  })
})

describe('client-dev-overlay with Cache Components', () => {
  const { next, isTurbopack } = nextTestSetup({
    files: {
      pages: new FileRef(join(__dirname, 'pages')),
      'next.config.js': `
        module.exports = {
          cacheComponents: true,
        }
      `,
    },
    env: {
      __NEXT_DEV_INDICATOR_COOLDOWN_MS: '0',
    },
  })

  it('should show Cache Components as enabled in the devtools menu', async () => {
    const browser = await next.browser('/')

    const devToolsIndicator = await waitForDevToolsIndicator(browser)
    try {
      await devToolsIndicator.click()
    } catch (cause) {
      const error = new Error('No DevTools Indicator to open.', { cause })
      throw error
    }

    const devtoolsMenu = await browser.elementByCss('#nextjs-dev-tools-menu')
    const menuText = await devtoolsMenu.innerText()

    // Should include Cache Components
    expect(menuText).toContain('Cache Components')
    expect(menuText).toContain('Enabled')

    // Should also include Turbopack info
    if (isTurbopack) {
      expect(menuText).toMatchInlineSnapshot(`
       "Issues
       1
       Route
       Static
       Bundler
       Turbopack
       Cache Components
       Enabled
       Preferences"
      `)
    } else {
      expect(menuText).toMatchInlineSnapshot(`
       "Issues
       1
       Route
       Static
       Bundler
       Webpack
       Cache Components
       Enabled
       Preferences"
      `)
    }
  })
})