-
Notifications
You must be signed in to change notification settings - Fork 667
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6f6e609
commit 02bd183
Showing
15 changed files
with
315 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 0 additions & 7 deletions
7
exercises/05.calculations/01.problem.use-memo/tests/todo.test.js
This file was deleted.
Oops, something went wrong.
42 changes: 42 additions & 0 deletions
42
exercises/05.calculations/01.problem.use-memo/tests/use-memo.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { test, expect } from '@playwright/test' | ||
|
||
test('searchCities should not be called when clicking force rerender', async ({ | ||
page, | ||
}) => { | ||
await page.route('**/cities/index.ts', async (route) => { | ||
const response = await route.fetch() | ||
let content = await response.text() | ||
content = content.replace( | ||
'export function searchCities', | ||
'function searchCities', | ||
) | ||
const instrumentedCode = ` | ||
// code below is added by the test: | ||
window.__epicshop = { clearSearchCitiesCalls: () => window.__epicshop.searchCitiesCalls = [] } | ||
window.__epicshop.clearSearchCitiesCalls() | ||
function searchCitiesInstrumented(...args) { | ||
window.__epicshop.searchCitiesCalls.push(args) | ||
return searchCities(...args) | ||
} | ||
export { searchCitiesInstrumented as searchCities } | ||
` | ||
content = `${content}\n\n${instrumentedCode}` | ||
|
||
route.fulfill({ body: content, headers: response.headers() }) | ||
}) | ||
await page.goto('/') | ||
await page.waitForLoadState('networkidle') | ||
|
||
await page.evaluate(() => (window as any).__epicshop.clearSearchCitiesCalls()) | ||
await page.getByRole('button', { name: /force/i }).click() | ||
const searchCitiesCalls: Array<Array<string>> = await page.evaluate( | ||
() => (window as any).__epicshop.searchCitiesCalls, | ||
) | ||
|
||
expect( | ||
searchCitiesCalls, | ||
'🚨 searchCities was called when clicking force rerender. Because nothing changed in the user input when clicking that button, searchCities should not have been called. Wrap searchCities inside useMemo with the input as a dependency to fix this.', | ||
).toHaveLength(0) | ||
}) |
7 changes: 0 additions & 7 deletions
7
exercises/05.calculations/01.solution.use-memo/tests/todo.test.js
This file was deleted.
Oops, something went wrong.
42 changes: 42 additions & 0 deletions
42
exercises/05.calculations/01.solution.use-memo/tests/use-memo.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { test, expect } from '@playwright/test' | ||
|
||
test('searchCities should not be called when clicking force rerender', async ({ | ||
page, | ||
}) => { | ||
await page.route('**/cities/index.ts', async (route) => { | ||
const response = await route.fetch() | ||
let content = await response.text() | ||
content = content.replace( | ||
'export function searchCities', | ||
'function searchCities', | ||
) | ||
const instrumentedCode = ` | ||
// code below is added by the test: | ||
window.__epicshop = { clearSearchCitiesCalls: () => window.__epicshop.searchCitiesCalls = [] } | ||
window.__epicshop.clearSearchCitiesCalls() | ||
function searchCitiesInstrumented(...args) { | ||
window.__epicshop.searchCitiesCalls.push(args) | ||
return searchCities(...args) | ||
} | ||
export { searchCitiesInstrumented as searchCities } | ||
` | ||
content = `${content}\n\n${instrumentedCode}` | ||
|
||
route.fulfill({ body: content, headers: response.headers() }) | ||
}) | ||
await page.goto('/') | ||
await page.waitForLoadState('networkidle') | ||
|
||
await page.evaluate(() => (window as any).__epicshop.clearSearchCitiesCalls()) | ||
await page.getByRole('button', { name: /force/i }).click() | ||
const searchCitiesCalls: Array<Array<string>> = await page.evaluate( | ||
() => (window as any).__epicshop.searchCitiesCalls, | ||
) | ||
|
||
expect( | ||
searchCitiesCalls, | ||
'🚨 searchCities was called when clicking force rerender. Because nothing changed in the user input when clicking that button, searchCities should not have been called. Wrap searchCities inside useMemo with the input as a dependency to fix this.', | ||
).toHaveLength(0) | ||
}) |
99 changes: 99 additions & 0 deletions
99
exercises/06.rerenders/01.problem.memo/tests/memoized.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { test, expect } from '@playwright/test' | ||
|
||
test('Only ListItem should not rerender when clicking force rerender', async ({ | ||
page, | ||
}) => { | ||
await page.route('/', async (route) => { | ||
const request = route.request() | ||
if (request.resourceType() !== 'document') return route.continue() | ||
const response = await route.fetch() | ||
|
||
let html = await response.text() | ||
const scriptToInject = ` | ||
<script> | ||
let internals | ||
function enhanceExistingHook(existingHook) { | ||
const originalInject = existingHook.inject | ||
existingHook.inject = (injectedInternals) => { | ||
internals = injectedInternals | ||
// Returning a number as React expects a renderer ID | ||
return originalInject?.call(existingHook, injectedInternals) ?? 1 | ||
} | ||
return existingHook | ||
} | ||
function createMinimalHook() { | ||
return { | ||
renderers: [], | ||
supportsFiber: true, | ||
inject: (injectedInternals) => { | ||
internals = injectedInternals | ||
return 1 // Returning a number as React expects a renderer ID | ||
}, | ||
onCommitFiberRoot: () => {}, | ||
onCommitFiberUnmount: () => {}, | ||
} | ||
} | ||
async function getComponentCalls(cb) { | ||
const componentNames = [] | ||
if (!internals) { | ||
throw new Error('🚨 React DevTools is not available') | ||
} | ||
internals.enableProfilerTimer = true | ||
internals.enableProfilerCommitHooks = true | ||
internals.injectProfilingHooks({ | ||
markComponentRenderStarted: (fiber) => { | ||
componentNames.push(fiber.type.name || 'Anonymous') | ||
}, | ||
}) | ||
await cb() | ||
internals.enableProfilerTimer = false | ||
internals.enableProfilerCommitHooks = false | ||
internals.injectProfilingHooks(null) | ||
return componentNames | ||
} | ||
window.getComponentCalls = getComponentCalls | ||
if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__) { | ||
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = enhanceExistingHook( | ||
window.__REACT_DEVTOOLS_GLOBAL_HOOK__, | ||
) | ||
} else { | ||
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = createMinimalHook() | ||
} | ||
</script> | ||
` | ||
html = html.replace('<head>', `<head>${scriptToInject}`) | ||
route.fulfill({ body: html, headers: { 'content-type': 'text/html' } }) | ||
}) | ||
|
||
await page.goto('/') | ||
await page.waitForLoadState('networkidle') | ||
|
||
const calledComponents: Array<string> = await page.evaluate(() => | ||
(window as any).getComponentCalls(() => { | ||
document.querySelector('button')?.click() | ||
}), | ||
) | ||
|
||
expect( | ||
calledComponents, | ||
'🚨 The ListItem component was rendered when clicking force render. Use the `memo` utility from React on the ListItem component to prevent this.', | ||
).not.toContain('ListItem') | ||
}) | ||
|
||
declare global { | ||
interface Window { | ||
__REACT_DEVTOOLS_GLOBAL_HOOK__?: any | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.