From afade4a5d3adc3a03a2a13642c2b5ded2f07e492 Mon Sep 17 00:00:00 2001 From: Oliver Eyton-Williams Date: Thu, 30 Jan 2025 08:43:44 +0100 Subject: [PATCH] refactor(curriculum): remove Enzyme from reuseable profile (#58480) Co-authored-by: Jessica Wilkins <67210629+jdwilkin4@users.noreply.github.com> --- .../674ef11f75254548672d998c.md | 22 ++---- .../674ef2d357676e50e4691658.md | 18 ++--- .../674ef2d357676e50e4691659.md | 68 ++++++++----------- .../674ef2d357676e50e469165a.md | 15 ++-- .../674ef2d357676e50e469165b.md | 30 +++++--- .../674ef2d357676e50e469165c.md | 47 +++++++------ .../674ef2d357676e50e469165d.md | 36 +++++----- .../browser-scripts/frame-runner.ts | 17 +++++ 8 files changed, 141 insertions(+), 112 deletions(-) diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef11f75254548672d998c.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef11f75254548672d998c.md index 274e512a793..69fc6e473d1 100644 --- a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef11f75254548672d998c.md +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef11f75254548672d998c.md @@ -19,26 +19,18 @@ Also, return a pair of parentheses with an empty string inside of them for now. You should create and export a `Card` functional component. Don't forget to return an empty string inside it. ```js -const mockedComponent = Enzyme.mount( - React.createElement(window.index.Card) -); - -assert.lengthOf(mockedComponent.find('Card'), 1); +assert.isFunction(window.index.Card); ``` Your `Card` component should have a `name`, `title`, and `bio` props. ```js -const mockedComponent = Enzyme.mount( - React.createElement(window.index.Card) -); - -const componentFunction = mockedComponent.type(); -const propsSource = componentFunction.toString(); - -assert.match(propsSource, /\bname\b/); -assert.match(propsSource, /\btitle\b/); -assert.match(propsSource, /\bbio\b/); +const functionRegex = __helpers.functionRegex("Card", null, { capture: true }); +const match = code.match(functionRegex); +const functionDefinition = match[0]; +assert.match(functionDefinition, /\{[^{]*name[^{]*\}/); +assert.match(functionDefinition, /\{[^{]*title[^{]*\}/); +assert.match(functionDefinition, /\{[^{]*bio[^{]*\}/); ``` # --seed-- diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691658.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691658.md index cd896b45aaf..5cea32aa91e 100644 --- a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691658.md +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691658.md @@ -11,21 +11,23 @@ Inside the return, remove the empty string, then create a `div` element with a ` # --hints-- -You should not have an empty string in your Card component. +Your `Card` component should not render an empty string. ```js -assert.notMatch(code, /""/) +async () => { + const testElem = await __prepTestComponent(window.index.Card); + assert.notEqual(testElem.innerHTML, ''); +} ``` You should create a `div` element with the `className` `card` at the top level of your `Card` component. ```js -const mockedComponent = Enzyme.mount( - React.createElement(window.index.Card) -); - -assert.equal(mockedComponent.childAt(0).type(), 'div'); -assert.isTrue(mockedComponent.childAt(0).hasClass('card')); +async () => { + const testElem = await __prepTestComponent(window.index.Card); + const div = testElem.firstElementChild; + assert.include([...div?.classList], 'card'); +} ``` # --seed-- diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691659.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691659.md index e2dd5790f06..c3dd6980cb5 100644 --- a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691659.md +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691659.md @@ -16,64 +16,56 @@ Also, inside the `div`, create a paragraph with the `className` of `card-title` You should create an `h2` element inside your `div` element. ```js -const mockedComponent = Enzyme.mount( - React.createElement(window.index.Card) -); - -const h2Elem = mockedComponent.find('h2'); - -assert.equal(h2Elem.parent().type(), 'div'); +async () => { + const testElem = await __prepTestComponent(window.index.Card); + const h2Elem = testElem.querySelector('div > h2'); + assert.exists(h2Elem); +} ``` Your `h2` element should have `{name}` as its text content. ```js -const mockedComponent = Enzyme.mount( - React.createElement(window.index.Card, { name: 'nameVal' }) -); - -const h2Elem = mockedComponent.find('h2'); - -assert.equal(h2Elem.text(), 'nameVal'); +async () => { + const testElem = await __prepTestComponent(window.index.Card, { name: 'nameVal' }); + const h2Elem = testElem.querySelector('div > h2'); + // trimming because we don't need to be picky about whitespace + assert.equal(h2Elem.textContent.trim(), 'nameVal'); +} ``` You should create a `p` tag with the `className` of `card-title` inside your `div` element. ```js -const mockedComponent = Enzyme.mount( - React.createElement(window.index.Card) -); - -const pElem = mockedComponent.find('p.card-title'); - -assert.equal(pElem.parent().type(), 'div'); +async () => { + const testElem = await __prepTestComponent(window.index.Card); + const pElem = testElem.querySelector('div > p.card-title'); + assert.exists(pElem); +} ``` Your `p` element with `className` of `card-title` should have `{title}` as its text content. ```js -const mockedComponent = Enzyme.mount( - React.createElement(window.index.Card, { title: 'titleVal' }) -); - -const pElem = mockedComponent.find('p.card-title'); - -assert.equal(pElem.text(), 'titleVal'); -assert.equal(pElem.parent().type(), 'div'); +async () => { + const testElem = await __prepTestComponent(window.index.Card, { title: 'titleVal' }); + const pElem = testElem.querySelector('div > p.card-title'); + // trimming because we don't need to be picky about whitespace + assert.equal(pElem.textContent.trim(), 'titleVal'); +} ``` You should create another `p` element with `{bio}` as its text. ```js -const mockedComponent = Enzyme.mount( - React.createElement(window.index.Card, { bio: 'bioVal' }) -); - -const secondPElem = mockedComponent.children().childAt(2) - -assert.equal(secondPElem.type(), 'p') -assert.equal(secondPElem.text(), 'bioVal'); -assert.equal(secondPElem.parent().type(), 'div'); +async () => { + const testElem = await __prepTestComponent(window.index.Card, {title: 'titleVal', bio: 'bioVal' }); + // trimming because we don't need to be picky about whitespace + const texts = [...testElem.querySelectorAll('div > p')].map((p) => p.textContent.trim()); + const bioText = texts.find((text) => text.includes('bioVal')); + assert.exists(bioText); + assert.equal(bioText, 'bioVal'); +} ``` # --seed-- diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165a.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165a.md index 7e92584a83c..a79be2c4144 100644 --- a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165a.md +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165a.md @@ -16,11 +16,18 @@ Inside the `App` component, return a pair of parentheses containing an empty str You should export an `App` functional component. ```js -const mockedComponent = Enzyme.mount( - React.createElement(window.index.App) -); +assert.isFunction(window.index.App); +``` -assert.lengthOf(mockedComponent.find('App'), 1); +Your `App` component should return an empty string. + +```js +// This isn't a perfect test, since various return values are converted into +// empty strings, but it has to be a valid React component. +async() => { + const testElem = await __prepTestComponent(window.index.App); + assert.equal(testElem.textContent, ''); +} ``` # --seed-- diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165b.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165b.md index db3fbd1070c..60dc491b9ca 100644 --- a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165b.md +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165b.md @@ -11,23 +11,35 @@ Inside your return statement, replace the empty string with a `div` element with # --hints-- -You should not have an empty string in your Card component. +Your `App` component should not render an empty string. ```js -assert.notMatch(code, /""/) +async () => { + const testElem = await __prepTestComponent(window.index.App); + assert.notEqual(testElem.innerHTML, ''); +} ``` -You should create a `div` element with the class `flex-container`. +Your `App` component should return a single `div` element. ```js -const mockedComponent = Enzyme.mount( - React.createElement(window.index.App) -); - -assert.equal(mockedComponent.childAt(0).type(), 'div'); -assert.isTrue(mockedComponent.childAt(0).hasClass('flex-container')); +async () => { + const testElem = await __prepTestComponent(window.index.App); + assert.equal(testElem.firstElementChild?.tagName, 'DIV'); + assert.lengthOf(testElem.children, 1); +} ``` +Your `div` should have a `className` property of `flex-container`. + +```js +async () => { + const testElem = await __prepTestComponent(window.index.App); + assert.include([...testElem.firstElementChild.classList], 'flex-container'); +} +``` + + # --seed-- ## --seed-contents-- diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165c.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165c.md index ebe73d4c75b..bf00e39fa41 100644 --- a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165c.md +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165c.md @@ -7,41 +7,48 @@ dashedName: step-6 # --description-- -Now, use the `Card` component with the `name` prop set to `"Mark"`, the `title` prop set to `"Frontend developer"`, and the `bio` set to `"I like to work with different frontend technologies and play video games."` +Now, put the `Card` component into your `App` component. Give it the `name` prop set to `"Mark"`, the `title` prop set to `"Frontend developer"`, and the `bio` set to `"I like to work with different frontend technologies and play video games."` # --hints-- -You should use the `Card` component as an element with a `name` prop set to `"Mark"`. +You should use the `Card` component in your `App` component. ```js -const mockedComponent = Enzyme.mount(React.createElement(window.index.App)); -const cardItem = mockedComponent.find(window.index.Card); - -const cardItemsProps = cardItem.props() - -assert.equal(cardItem.props().name, "Mark") +async () => { + const testElem = await __prepTestComponent(window.index.App); + const card = testElem.querySelector('.card'); + assert.exists(card); +} ``` -You should use the `Card` component as an element with a `title` prop set to `"Frontend developer"`. +You should pass the `name` prop with value `"Mark"` to your `Card` component. ```js -const mockedComponent = Enzyme.mount(React.createElement(window.index.App)); -const cardItem = mockedComponent.find(window.index.Card); - -const cardItemsProps = cardItem.props() - -assert.equal(cardItem.props().title, "Frontend developer") +async () => { + const testElem = await __prepTestComponent(window.index.App); + const card = testElem.querySelector('.card'); + assert.equal(card.querySelector('h2').textContent, 'Mark'); +} ``` -You should use the `Card` component as an element with a `bio` prop set to `"I like to work with different frontend technologies and play video games."`. +You should pass the `title` prop with value `"Frontend developer"` to your `Card` component. ```js -const mockedComponent = Enzyme.mount(React.createElement(window.index.App)); -const cardItem = mockedComponent.find(window.index.Card); +async () => { + const testElem = await __prepTestComponent(window.index.App); + const card = testElem.querySelector('.card'); + assert.equal(card.querySelector('.card-title').textContent, 'Frontend developer'); +} +``` -const cardItemsProps = cardItem.props() +You should pass the `bio` prop with value `"I like to work with different frontend technologies and play video games."` to your `Card` component. -assert.equal(cardItem.props().bio, "I like to work with different frontend technologies and play video games.") +```js +async () => { + const testElem = await __prepTestComponent(window.index.App); + const card = testElem.querySelector('.card'); + assert.equal(card.querySelector('p:last-child').textContent, 'I like to work with different frontend technologies and play video games.'); +} ``` # --seed-- diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165d.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165d.md index 7b1d60ed468..02da45b5888 100644 --- a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165d.md +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165d.md @@ -19,34 +19,34 @@ With that, your reusable profile card component project is complete! # --hints-- +You should not modify the existing `Card` component in your `App` component. + +```js +const zerothCard = document.querySelectorAll('.card')[0]; + +assert.equal(zerothCard.querySelector('h2').textContent, 'Mark'); +assert.equal(zerothCard.querySelector('.card-title').textContent, 'Frontend developer'); +assert.equal(zerothCard.querySelector('p:last-child').textContent, 'I like to work with different frontend technologies and play video games.'); +``` + You should use the `Card` component as an element with the appropriate props and first card values. ```js -const mockedComponent = Enzyme.mount(React.createElement(window.index.App)); -const cardItems = mockedComponent.find(window.index.Card); +const firstCard = document.querySelectorAll('.card')[1]; -const propsList = cardItems.map(item => item.props()); - -assert.deepEqual(propsList[1], { - name: 'Tiffany', - title: 'Engineering manager', - bio: 'I have worked in tech for 15 years and love to help people grow in this industry.' -}); +assert.equal(firstCard.querySelector('h2').textContent, 'Tiffany'); +assert.equal(firstCard.querySelector('.card-title').textContent, 'Engineering manager'); +assert.equal(firstCard.querySelector('p:last-child').textContent, 'I have worked in tech for 15 years and love to help people grow in this industry.'); ``` You should use the `Card` component as an element with the appropriate props and second card values. ```js -const mockedComponent = Enzyme.mount(React.createElement(window.index.App)); -const cardItems = mockedComponent.find(window.index.Card); +const secondCard = document.querySelectorAll('.card')[2]; -const propsList = cardItems.map(item => item.props()); - -assert.deepEqual(propsList[2], { - name: 'Doug', - title: 'Backend developer', - bio: 'I have been a software developer for over 20 years and I love working with Go and Rust.' -}); +assert.equal(secondCard.querySelector('h2').textContent, 'Doug'); +assert.equal(secondCard.querySelector('.card-title').textContent, 'Backend developer'); +assert.equal(secondCard.querySelector('p:last-child').textContent, 'I have been a software developer for over 20 years and I love working with Go and Rust.'); ``` # --seed-- diff --git a/tools/client-plugins/browser-scripts/frame-runner.ts b/tools/client-plugins/browser-scripts/frame-runner.ts index e1f68df1846..a389de452a3 100644 --- a/tools/client-plugins/browser-scripts/frame-runner.ts +++ b/tools/client-plugins/browser-scripts/frame-runner.ts @@ -58,6 +58,23 @@ async function initTestFrame(e: InitTestFrameArg = { code: {} }) { return o; }; + const __prepTestComponent = async ( + component: React.ComponentType | React.FunctionComponent | string, + props: Record + ) => { + const testDiv = document.createElement('div'); + const createdElement = window.React.createElement(component, props); + + // @ts-expect-error the React version is determined at runtime so we can't define the types here + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/require-await + await window.React?.act(async () => { + // @ts-expect-error Same for ReactDOM as for React + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + window.ReactDOM?.createRoot(testDiv).render(createdElement); + }); + return testDiv; + }; + const { default: chai } = await import(/* webpackChunkName: "chai" */ 'chai'); const assert = chai.assert; const __helpers = helpers;