Skip to content

Commit

Permalink
fix: add tests and structure
Browse files Browse the repository at this point in the history
  • Loading branch information
matthprost committed Jan 22, 2025
1 parent 572842f commit aecfaf8
Show file tree
Hide file tree
Showing 15 changed files with 411 additions and 66 deletions.
84 changes: 83 additions & 1 deletion e2e/App.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,85 @@
const App = () => <>Welcome !</>
import { ThemeProvider } from '@emotion/react'
import '@ultraviolet/fonts/fonts.css'
import { consoleLightTheme } from '@ultraviolet/themes'
import { Text } from '@ultraviolet/ui'
import { lazy } from 'react'
import {
Link as ReactRouterLink,
Route,
BrowserRouter as Router,
Routes,
} from 'react-router-dom'

/**
* We get all the render.tsx in tests folder and generate individual pages / routes to render the content.
*/
const modules = import.meta.glob('./tests/**/*.tsx')
const pagesToRender = Object.keys(modules)
.map(path =>
path.includes('render.tsx')
? {
name: path.replace('.tsx', ''),
Component: lazy(
() => import(`./tests/${path?.split('/')[2]}/render.tsx`),
),
}
: null,
)
.filter(Boolean)
/**
* ----------------------------------------
*/

const WelcomePage = () => (
<>
<Text as="p" variant="headingStrong">
Welcome to the Ultraviolet E2E testing suite!
</Text>
<Text as="p" variant="body">
This page is a placeholder for the available pages to test. Please read
the README.md file for more information.
</Text>
<Text as="p" variant="body">
Available pages to test:
</Text>
<ul>
{pagesToRender.map(path => (
<li key={path?.name}>
<ReactRouterLink
to={{ pathname: path?.name?.split('/')[2]?.toLowerCase() ?? '' }}
>
{path?.name?.split('/')[2]?.toLowerCase()}
</ReactRouterLink>
</li>
))}
</ul>
</>
)

const App = () => (
<ThemeProvider theme={consoleLightTheme}>
<Router>
<Routes>
<Route path="/" element={<WelcomePage />} />
<Route path="/test" element={<WelcomePage />} />
{pagesToRender.map(path => {
const Element = path?.Component

if (Element) {
return (
<Route
key={path?.name}
path={path?.name?.split('/')[2]?.toLowerCase()}
element={<Element />}
/>
)
}

return null
})}
</Routes>
</Router>
</ThemeProvider>
)

export default App
43 changes: 43 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# E2E Testing

This project uses playwright to run end-to-end tests. As we are testing a library we need to setup a test environment
for rendering the component and the test file for each test case.

> [!WARNING]
> Not all components needs a e2e test. We only want to test specific interaction that are not covered by the unit tests.
> E2E tests can be used for testing a mix of component and see their interaction. Example: select input within a modal and vice versa.
## Structure
```
e2e
├── tests
│ ├── useCase1
│ │ ├── render.tsx # This file will render the component. File name is important!
│ │ └── test.spec.ts # This file will contain the test cases
│ ├── useCase2
│ │ ├── render.tsx
│ │ └── test.spec.ts
└── playwright.config.ts
```

What you need to understand is that for each test case you need to create a file `render.tsx` that will render the component.
And a file `test.spec.ts` that will contain the test cases run by playwright.

## Running the tests

To run the tests you can use the following command:

```bash
pnpm start # This will start the server with all the components

# In another terminal
pnpm test:e2e # This will run the tests
```

> [!NOTE]
> Test are run on 3 different browsers: `chromium`, `firefox` and `webkit`.
## Writing the tests

To write the tests you can copy and paste the `template` folder in `tests/template`. You can then rename the folder and change the content
of the files to match the test case you want to test.
11 changes: 11 additions & 0 deletions e2e/mocks/mockErrors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const mockErrors = {
maxDate: () => `Date must be lower than ...`,
maxLength: () => `This field should have a length lower than ...`,
minDate: () => `Date must be greater than ...`,
minLength: () => `This field should have a length greater than ...`,
pattern: () => `This field should match the regex`,
required: () => 'This field is required',
max: () => `This field is too high (maximum is : ...)`,
min: () => `This field is too low (minimum is: ...)`,
isInteger: () => `Incorrect field`,
}
12 changes: 9 additions & 3 deletions e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,28 @@
"type": "module",
"scripts": {
"start": "vite",
"e2e": "playwright test",
"e2e:debug": "playwright test --ui",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "11.14.0",
"@emotion/styled": "11.14.0",
"@ultraviolet/fonts": "workspace:*",
"@ultraviolet/form": "workspace:*",
"@ultraviolet/icons": "workspace:*",
"@ultraviolet/themes": "workspace:*",
"@ultraviolet/ui": "workspace:*",
"react": "19.0.0",
"react-dom": "19.0.0"
"react-dom": "19.0.0",
"react-router-dom": "7.1.3"
},
"devDependencies": {
"@emotion/babel-plugin": "11.13.5",
"@eslint/js": "9.17.0",
"@playwright/test": "1.49.1",
"@types/react": "19.0.5",
"@types/react-dom": "19.0.3",
"@vitejs/plugin-react": "4.3.4",
Expand All @@ -29,7 +36,6 @@
"globals": "15.14.0",
"typescript": "5.7.3",
"typescript-eslint": "^8.19.1",
"vite": "6.0.7",
"@emotion/babel-plugin": "11.13.5"
"vite": "6.0.7"
}
}
71 changes: 71 additions & 0 deletions e2e/playwright-report/index.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion e2e/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { defineConfig, devices } from '@playwright/test'

const isCI = process.env['CI']

const baseURL = process.env['BASE_URL'] ?? 'http://localhost:6006'
const baseURL = process.env['BASE_URL'] ?? 'http://localhost:5173'

const times = {
'1min': 60 * 1000,
Expand Down
4 changes: 4 additions & 0 deletions e2e/test-results/.last-run.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"status": "passed",
"failedTests": []
}
58 changes: 58 additions & 0 deletions e2e/tests/componentsWithinModal/render.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
Form,
SelectInputFieldV2,
TextInputFieldV2,
useForm,
} from '@ultraviolet/form'
import { Button, Modal, TextInputV2 } from '@ultraviolet/ui'
import { useState } from 'react'
import { mockErrors } from '../../mocks/mockErrors'

const Render = () => {
const methods = useForm<{ lastName: ''; color: '' }>()
const [firstName, setFirstName] = useState<string>()

return (
<Modal disclosure={<Button>Open Modal</Button>}>
<TextInputV2
label="First name"
onChangeValue={setFirstName}
value={firstName}
/>
<div data-testid="input-value">{firstName}</div>

<Form errors={mockErrors} onSubmit={() => {}} methods={methods}>
<TextInputFieldV2
name="lastName"
label="Last name"
control={methods.control}
/>
<div data-testid="form-content">{methods.watch().lastName}</div>

<div style={{ display: 'flex', flexDirection: 'column', gap: 32 }}>
<SelectInputFieldV2
name="color"
label="Color"
size="medium"
control={methods.control}
options={[
{
label: 'Red',
value: 'red',
},
{
label: 'Green',
value: 'green',
},
{
label: 'Blue',
value: 'blue',
},
]}
/>
</div>
</Form>
</Modal>
)
}
export default Render
40 changes: 40 additions & 0 deletions e2e/tests/componentsWithinModal/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { expect, test } from '@playwright/test'

test('open modal, fill text inputs, close modal', async ({ page, baseURL }) => {
await page.goto(`${baseURL}/componentsWithinModal`)
await page.getByRole('button', { name: 'Open Modal' }).click()
await page.getByLabel('First name').click()
await page.getByLabel('First name').fill('Test First Name')

await expect(page.locator('div[data-testid="input-value"]')).toHaveText(
'Test First Name',
)

await page.getByLabel('Last name').click()
await page.getByLabel('Last name').fill('Test Last Name')

await expect(page.locator('div[data-testid="form-content"]')).toHaveText(
'Test Last Name',
)
})

test('open modal, click and fill on select input, close modal', async ({
page,
baseURL,
}) => {
await page.goto(`${baseURL}/componentsWithinModal`)
await page.getByRole('button', { name: 'Open Modal' }).click()
await page.getByLabel('First name').click()
await page.getByLabel('First name').fill('Test First Name')

await expect(page.locator('div[data-testid="input-value"]')).toHaveText(
'Test First Name',
)

await page.getByLabel('Last name').click()
await page.getByLabel('Last name').fill('Test Last Name')

await expect(page.locator('div[data-testid="form-content"]')).toHaveText(
'Test Last Name',
)
})
9 changes: 0 additions & 9 deletions e2e/tests/default.spec.ts

This file was deleted.

22 changes: 0 additions & 22 deletions e2e/tests/ui/checkbox.spec.ts

This file was deleted.

26 changes: 0 additions & 26 deletions e2e/tests/ui/list.spec.ts

This file was deleted.

18 changes: 14 additions & 4 deletions e2e/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
{
"extends": "../tsconfig.json",
"$schema": "http://json.schemastore.org/tsconfig",
"extends": "@scaleway/tsconfig",
"compilerOptions": {
"baseUrl": ".",
"allowImportingTsExtensions": true
"target": "esnext",
"module": "esnext",
"jsx": "preserve",
"jsxImportSource": "@emotion/react",
"allowImportingTsExtensions": true,
"forceConsistentCasingInFileNames": true,
"noPropertyAccessFromIndexSignature": false,
"noEmit": true,
"isolatedModules": true,
"skipLibCheck": true,
"types": ["vite/client"]
},
"include": ["src", "../../global.d.ts", "emotion.d.ts"],
"include": ["global.d.ts", "emotion.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules", "coverage", "dist"]
}
11 changes: 11 additions & 0 deletions e2e/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,15 @@ export default defineConfig({
},
}),
],
build: {
rollupOptions: {
external: ['fsevents'],
},
},
server: {
// Sends all requests to index.html if file not found
fs: {
allow: ['.'], // or paths as needed
},
},
})
Loading

0 comments on commit aecfaf8

Please sign in to comment.