Add some additional examples
This commit is contained in:
35
examples/directory/package.json
Normal file
35
examples/directory/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "directory",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite dev",
|
||||
"test": "vitest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/stevekinney/testing-javascript.git"
|
||||
},
|
||||
"author": "Steve Kinney <hello@stevekinney.net>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/stevekinney/testing-javascript/issues"
|
||||
},
|
||||
"homepage": "https://github.com/stevekinney/testing-javascript#readme",
|
||||
"devDependencies": {
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.5.0",
|
||||
"@testing-library/react": "^16.0.1",
|
||||
"@types/body-parser": "^1.19.5",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/react": "^18.3.6",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/testing-library__jest-dom": "^5.14.9",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"@vitest/ui": "^2.1.1",
|
||||
"msw": "^2.4.9",
|
||||
"vite": "^5.4.6",
|
||||
"vitest": "^2.1.1"
|
||||
}
|
||||
}
|
||||
11
examples/directory/src/get-user.js
Normal file
11
examples/directory/src/get-user.js
Normal file
@@ -0,0 +1,11 @@
|
||||
export const getUser = async (id) => {
|
||||
const response = await fetch(
|
||||
`https://jsonplaceholder.typicode.com/users/${id}`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch user');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
};
|
||||
5
examples/directory/src/mocks/handlers.js
Normal file
5
examples/directory/src/mocks/handlers.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import { http, HttpResponse } from 'msw';
|
||||
|
||||
// Hint: https://jsonplaceholder.typicode.com/users/:id
|
||||
|
||||
export const handlers = [];
|
||||
4
examples/directory/src/mocks/server.js
Normal file
4
examples/directory/src/mocks/server.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import { setupServer } from 'msw/node';
|
||||
import { handlers } from './handlers';
|
||||
|
||||
export const server = setupServer(...handlers);
|
||||
16
examples/directory/src/mocks/tasks.json
Normal file
16
examples/directory/src/mocks/tasks.json
Normal file
@@ -0,0 +1,16 @@
|
||||
[
|
||||
{
|
||||
"id": "test-1",
|
||||
"title": "Get a Phone Charger",
|
||||
"completed": true,
|
||||
"createdAt": "2024-09-19T08:30:00.711Z",
|
||||
"lastModified": "2024-09-19T08:30:00.711Z"
|
||||
},
|
||||
{
|
||||
"id": "test-2",
|
||||
"title": "Charge Your Phone",
|
||||
"completed": false,
|
||||
"createdAt": "2024-09-19T08:31:00.261Z",
|
||||
"lastModified": "2024-09-19T08:31:00.261Z"
|
||||
}
|
||||
]
|
||||
44
examples/directory/src/user.jsx
Normal file
44
examples/directory/src/user.jsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { getUser } from './get-user';
|
||||
|
||||
export const User = ({ id }) => {
|
||||
const [user, setUser] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
getUser(id).then(setUser);
|
||||
}, [setUser]);
|
||||
|
||||
if (!user) {
|
||||
return <p>Loading…</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<article data-testid={`user-${id}`}>
|
||||
<h2>{user.name}</h2>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Company</th>
|
||||
<td>{user.company.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<td>{user.username}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Email</th>
|
||||
<td>{user.email}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Phone</th>
|
||||
<td>{user.phone}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Website</th>
|
||||
<td>{user.website}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
);
|
||||
};
|
||||
10
examples/directory/src/user.test.jsx
Normal file
10
examples/directory/src/user.test.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { screen, render } from '@testing-library/react';
|
||||
import { User } from './user';
|
||||
|
||||
it.skip('should render a user', async () => {
|
||||
// This calls an API. That's not great. We should mock it.
|
||||
const id = 1;
|
||||
render(<User id={id} />);
|
||||
|
||||
expect(user).toBeInTheDocument();
|
||||
});
|
||||
15
examples/directory/vite.config.ts
Normal file
15
examples/directory/vite.config.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { css } from 'css-configuration';
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
css,
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'happy-dom',
|
||||
setupFiles: ['@testing-library/jest-dom/vitest'],
|
||||
},
|
||||
});
|
||||
@@ -12,6 +12,7 @@ describe('LoginForm', async () => {
|
||||
});
|
||||
|
||||
it('should render a login form with a custom action', async () => {
|
||||
// Can you make sure that the form we render has an `action` attribute set to '/custom'?
|
||||
document.body.replaceChildren(createLoginForm({ action: '/custom' }));
|
||||
|
||||
const form = screen.getByRole('form', { name: /login/i });
|
||||
@@ -20,6 +21,7 @@ describe('LoginForm', async () => {
|
||||
});
|
||||
|
||||
it('should render a login form with a custom method', async () => {
|
||||
// Can you make sure that the form we render has a `method` attribute set to 'get'?
|
||||
document.body.replaceChildren(createLoginForm({ method: 'get' }));
|
||||
|
||||
const form = screen.getByRole('form', { name: /login/i });
|
||||
@@ -28,6 +30,9 @@ describe('LoginForm', async () => {
|
||||
});
|
||||
|
||||
it('should render a login form with a custom submit handler', async () => {
|
||||
// We'll do this one later. Don't worry about it for now.
|
||||
// If it *is* later, then you should worry about it.
|
||||
// Can you make sure that the form we render has a submit handler that calls a custom function?
|
||||
const onSubmit = vi.fn();
|
||||
document.body.replaceChildren(createLoginForm({ onSubmit }));
|
||||
|
||||
|
||||
30
examples/element-factory/src/notification.jsx
Normal file
30
examples/element-factory/src/notification.jsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
export const Notification = () => {
|
||||
const [content, setContent] = useState('');
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
const showNotification = () => {
|
||||
console.log({ content });
|
||||
if (!content) return;
|
||||
setMessage(content);
|
||||
setTimeout(() => setMessage(''), 3000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label>
|
||||
Message Content
|
||||
<input
|
||||
type="text"
|
||||
value={content}
|
||||
onChange={(event) => setContent(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<button onClick={showNotification}>Show Notification</button>
|
||||
|
||||
{message && <p data-testid="message">{message}</p>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
73
examples/element-factory/src/notification.test.jsx
Normal file
73
examples/element-factory/src/notification.test.jsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import { vi } from 'vitest';
|
||||
import { render, screen, act } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { Notification } from './notification';
|
||||
|
||||
describe('Notification', () => {
|
||||
beforeEach(() => {
|
||||
render(<Notification />);
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should render a notification', async () => {
|
||||
const input = screen.getByRole('textbox', { name: /message content/i });
|
||||
const button = screen.getByRole('button', { name: /show notification/i });
|
||||
|
||||
expect(input).toBeInTheDocument();
|
||||
expect(button).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.only('should show a notification', async () => {
|
||||
const input = screen.getByRole('textbox', { name: /message content/i });
|
||||
const button = screen.getByRole('button', { name: /show notification/i });
|
||||
|
||||
await act(async () => {
|
||||
await userEvent.type(input, 'Hello, world!');
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await userEvent.click(button);
|
||||
});
|
||||
|
||||
const message = await screen.findByTestId('message');
|
||||
|
||||
expect(message).toHaveTextContent('Hello, world!');
|
||||
});
|
||||
|
||||
it('should not show a notification if there is no content', async () => {
|
||||
const button = screen.getByRole('button', { name: /show notification/i });
|
||||
|
||||
await act(async () => {
|
||||
await userEvent.click(button);
|
||||
});
|
||||
|
||||
const message = screen.queryByTestId('message');
|
||||
|
||||
expect(message).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should hide a notification after 5 seconds', async () => {
|
||||
const input = screen.getByRole('textbox', { name: /message content/i });
|
||||
const button = screen.getByRole('button', { name: /show notification/i });
|
||||
|
||||
await act(async () => {
|
||||
await userEvent.type(input, 'Hello, world!');
|
||||
await userEvent.click(button);
|
||||
});
|
||||
|
||||
const message = screen.getByTestId('message');
|
||||
|
||||
expect(message).toHaveTextContent('Hello, world!');
|
||||
|
||||
await act(async () => {
|
||||
vi.advanceTimersByTime(5000);
|
||||
});
|
||||
|
||||
expect(message).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -5,8 +5,8 @@ import react from '@vitejs/plugin-react';
|
||||
export default defineConfig({
|
||||
plugins: [svelte(), react()],
|
||||
test: {
|
||||
environment: 'happy-dom',
|
||||
globals: true,
|
||||
environment: 'happy-dom',
|
||||
setupFiles: ['@testing-library/jest-dom/vitest'],
|
||||
},
|
||||
});
|
||||
|
||||
6
examples/logjam/src/make-request.js
Normal file
6
examples/logjam/src/make-request.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export const makeRequest = async (url) => {
|
||||
if (!import.meta.env.API_KEY) {
|
||||
throw new Error('API_KEY is required');
|
||||
}
|
||||
return { data: 'Some data would go here.' };
|
||||
};
|
||||
3
examples/logjam/src/make-request.test.js
Normal file
3
examples/logjam/src/make-request.test.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import { makeRequest } from './make-request';
|
||||
|
||||
describe.todo('makeRequest', () => {});
|
||||
21
examples/scratchpad/fake-time.test.js
Normal file
21
examples/scratchpad/fake-time.test.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import { vi, describe, it, expect } from 'vitest';
|
||||
|
||||
vi.useFakeTimers();
|
||||
|
||||
function delay(callback) {
|
||||
setTimeout(() => {
|
||||
callback('Delayed');
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
describe('delay function', () => {
|
||||
it('should call callback after delay', () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
delay(callback);
|
||||
|
||||
vi.advanceTimersByTime(1000);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith('Delayed');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
import { it, expect } from 'vitest';
|
||||
|
||||
it('is a super simple test', () => {
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
26
examples/scratchpad/package.json
Normal file
26
examples/scratchpad/package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "scratchpad",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"main": "src/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vitest --ui",
|
||||
"test": "vitest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/stevekinney/testing-javascript.git"
|
||||
},
|
||||
"author": "Steve Kinney <hello@stevekinney.net>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/stevekinney/testing-javascript/issues"
|
||||
},
|
||||
"homepage": "https://github.com/stevekinney/testing-javascript#readme",
|
||||
"devDependencies": {
|
||||
"@vitest/ui": "^2.1.1",
|
||||
"vite": "^5.4.5",
|
||||
"vitest": "^2.1.1"
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,8 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
test: {
|
||||
environment: 'happy-dom',
|
||||
globals: true,
|
||||
environment: 'happy-dom',
|
||||
setupFiles: ['@testing-library/jest-dom/vitest'],
|
||||
},
|
||||
});
|
||||
|
||||
38
package-lock.json
generated
38
package-lock.json
generated
@@ -21,7 +21,8 @@
|
||||
"examples/utility-belt",
|
||||
"examples/strictly-speaking",
|
||||
"examples/element-factory",
|
||||
"examples/logjam"
|
||||
"examples/logjam",
|
||||
"examples/directory"
|
||||
],
|
||||
"devDependencies": {
|
||||
"prettier": "^3.3.3",
|
||||
@@ -110,6 +111,25 @@
|
||||
"vitest": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"examples/directory": {
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.5.0",
|
||||
"@testing-library/react": "^16.0.1",
|
||||
"@types/body-parser": "^1.19.5",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/react": "^18.3.6",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/testing-library__jest-dom": "^5.14.9",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"@vitest/ui": "^2.1.1",
|
||||
"msw": "^2.4.9",
|
||||
"vite": "^5.4.6",
|
||||
"vitest": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"examples/element-factory": {
|
||||
"name": "button-factory",
|
||||
"version": "1.0.0",
|
||||
@@ -159,7 +179,13 @@
|
||||
}
|
||||
},
|
||||
"examples/scratchpad": {
|
||||
"extraneous": true
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@vitest/ui": "^2.1.1",
|
||||
"vite": "^5.4.5",
|
||||
"vitest": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"examples/strictly-speaking": {
|
||||
"version": "1.0.0",
|
||||
@@ -2705,6 +2731,10 @@
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/directory": {
|
||||
"resolved": "examples/directory",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/dlv": {
|
||||
"version": "1.1.3",
|
||||
"dev": true,
|
||||
@@ -4831,6 +4861,10 @@
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/scratchpad": {
|
||||
"resolved": "examples/scratchpad",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"dev": true,
|
||||
|
||||
@@ -38,7 +38,8 @@
|
||||
"examples/utility-belt",
|
||||
"examples/strictly-speaking",
|
||||
"examples/element-factory",
|
||||
"examples/logjam"
|
||||
"examples/logjam",
|
||||
"examples/directory"
|
||||
],
|
||||
"devDependencies": {
|
||||
"prettier": "^3.3.3",
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
"jsx": "react-jsx",
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["examples/**/*.js", "examples/**/*.svelte", "examples/**/*.ts"],
|
||||
"include": [
|
||||
"examples/**/*.js",
|
||||
"examples/**/*.svelte",
|
||||
"examples/**/*.ts",
|
||||
"examples/directory/src/user.jsx"
|
||||
],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user