mirror of
https://github.com/LukeHagar/unicorn-utterances.git
synced 2025-12-09 21:07:49 +00:00
chore: setup initial MSW and test scaffolding for search page
This commit is contained in:
@@ -3,8 +3,11 @@
|
|||||||
|
|
||||||
// Used for __tests__/testing-library.js
|
// Used for __tests__/testing-library.js
|
||||||
// Learn more: https://github.com/testing-library/jest-dom
|
// Learn more: https://github.com/testing-library/jest-dom
|
||||||
|
require("whatwg-fetch");
|
||||||
require("@testing-library/jest-dom/jest-globals");
|
require("@testing-library/jest-dom/jest-globals");
|
||||||
|
|
||||||
|
global.plausible = null;
|
||||||
|
|
||||||
global.IntersectionObserver = class IntersectionObserver {
|
global.IntersectionObserver = class IntersectionObserver {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const { resolve } = require("path");
|
const { resolve } = require("path");
|
||||||
|
require('whatwg-fetch');
|
||||||
|
|
||||||
// Add any custom config to be passed to Jest
|
// Add any custom config to be passed to Jest
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
746
package-lock.json
generated
746
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,7 @@
|
|||||||
"social-previews:dev": "npm-run-all --parallel social-previews:dev:build social-previews:dev:server",
|
"social-previews:dev": "npm-run-all --parallel social-previews:dev:build social-previews:dev:server",
|
||||||
"epub": "tsx --tsconfig tsconfig.script.json build-scripts/generate-epubs.ts",
|
"epub": "tsx --tsconfig tsconfig.script.json build-scripts/generate-epubs.ts",
|
||||||
"tsc": "tsc --noEmit && astro check",
|
"tsc": "tsc --noEmit && astro check",
|
||||||
"test": "jest",
|
"test": "set DEBUG_PRINT_LIMIT=9999999999 && jest",
|
||||||
"prepare": "husky install && playwright install"
|
"prepare": "husky install && playwright install"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -51,6 +51,8 @@
|
|||||||
"@testing-library/dom": "^9.3.1",
|
"@testing-library/dom": "^9.3.1",
|
||||||
"@testing-library/jest-dom": "^6.0.0",
|
"@testing-library/jest-dom": "^6.0.0",
|
||||||
"@testing-library/preact": "^3.2.3",
|
"@testing-library/preact": "^3.2.3",
|
||||||
|
"@testing-library/user-event": "^14.4.3",
|
||||||
|
"@types/hast": "^3.0.0",
|
||||||
"@types/jest": "^29.5.2",
|
"@types/jest": "^29.5.2",
|
||||||
"@types/json5": "^2.2.0",
|
"@types/json5": "^2.2.0",
|
||||||
"@types/node": "^20.5.0",
|
"@types/node": "^20.5.0",
|
||||||
@@ -85,6 +87,7 @@
|
|||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"junk": "^4.0.1",
|
"junk": "^4.0.1",
|
||||||
"lint-staged": "^14.0.0",
|
"lint-staged": "^14.0.0",
|
||||||
|
"msw": "^1.2.3",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"plausible-tracker": "^0.3.8",
|
"plausible-tracker": "^0.3.8",
|
||||||
"playwright": "^1.37.0",
|
"playwright": "^1.37.0",
|
||||||
@@ -114,8 +117,8 @@
|
|||||||
"unist-util-replace-all-between": "^0.1.1",
|
"unist-util-replace-all-between": "^0.1.1",
|
||||||
"unist-util-visit": "^5.0.0",
|
"unist-util-visit": "^5.0.0",
|
||||||
"vercel": "^31.2.3",
|
"vercel": "^31.2.3",
|
||||||
"@types/hast": "^3.0.0",
|
"vite-plugin-svgr": "^3.2.0",
|
||||||
"vite-plugin-svgr": "^3.2.0"
|
"whatwg-fetch": "^3.6.17"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,ts,astro}": "eslint --cache --fix",
|
"*.{js,ts,astro}": "eslint --cache --fix",
|
||||||
|
|||||||
@@ -16,10 +16,8 @@ export const Picture = ({
|
|||||||
}: PictureProps) => {
|
}: PictureProps) => {
|
||||||
return (
|
return (
|
||||||
<picture class={`${className || ""}`}>
|
<picture class={`${className || ""}`}>
|
||||||
{picture.sources.map((attrs) => (
|
{picture?.sources.map((attrs) => <source {...attrs} />)}
|
||||||
<source {...attrs} />
|
<img {...((picture?.image as any) ?? {})} {...imgAttrs} alt={alt} />
|
||||||
))}
|
|
||||||
<img {...(picture.image as any)} {...imgAttrs} alt={alt} />
|
|
||||||
</picture>
|
</picture>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export const FilterSectionItem = ({
|
|||||||
const props = {
|
const props = {
|
||||||
isSelected: selected,
|
isSelected: selected,
|
||||||
onChange: onChange,
|
onChange: onChange,
|
||||||
|
"aria-label": label,
|
||||||
};
|
};
|
||||||
|
|
||||||
const state = useToggleState(props);
|
const state = useToggleState(props);
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export const SearchTopbar = ({
|
|||||||
>
|
>
|
||||||
<SearchInput
|
<SearchInput
|
||||||
id="search-bar"
|
id="search-bar"
|
||||||
|
aria-label="Search"
|
||||||
class={style.searchbar}
|
class={style.searchbar}
|
||||||
usedInPreact={true}
|
usedInPreact={true}
|
||||||
value={search}
|
value={search}
|
||||||
|
|||||||
69
src/views/search/search-page.spec.tsx
Normal file
69
src/views/search/search-page.spec.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { fireEvent, render, waitFor } from "@testing-library/preact";
|
||||||
|
import SearchPage, { ServerReturnType } from "./search-page";
|
||||||
|
import { rest } from "msw";
|
||||||
|
import { setupServer } from "msw/node";
|
||||||
|
import { MockPost } from "../../../__mocks__/data/mock-post";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
|
const user = userEvent.setup();
|
||||||
|
|
||||||
|
const server = setupServer();
|
||||||
|
|
||||||
|
beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
|
||||||
|
|
||||||
|
afterEach(() => server.resetHandlers());
|
||||||
|
|
||||||
|
afterAll(() => server.close());
|
||||||
|
|
||||||
|
function mockFetch(fn: (searchStr: string) => ServerReturnType) {
|
||||||
|
server.use(
|
||||||
|
rest.get<ServerReturnType>(
|
||||||
|
`/api/search`,
|
||||||
|
async (req, res, ctx) => {
|
||||||
|
const searchString = req.url.searchParams.get("query");
|
||||||
|
return res(ctx.json(fn(searchString)));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Search page", () => {
|
||||||
|
test("Should show initial results", () => {
|
||||||
|
const { getByText } = render(<SearchPage unicornProfilePicMap={[]} />);
|
||||||
|
|
||||||
|
expect(getByText("What would you like to find?")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Should show search results filtered by client", async () => {
|
||||||
|
mockFetch(() => ({
|
||||||
|
posts: [MockPost],
|
||||||
|
totalPosts: 1,
|
||||||
|
totalCollections: 0,
|
||||||
|
collections: [],
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { getByText, getByLabelText, debug, getByTestId } = render(
|
||||||
|
<SearchPage unicornProfilePicMap={[]} />,
|
||||||
|
);
|
||||||
|
const searchInput = getByLabelText("Search");
|
||||||
|
await user.type(searchInput, MockPost.title);
|
||||||
|
await user.type(searchInput, "{enter}");
|
||||||
|
await waitFor(() => expect(getByText(MockPost.title)).toBeInTheDocument());
|
||||||
|
});
|
||||||
|
|
||||||
|
test.todo("Should show error with 500");
|
||||||
|
test.todo("Should show 'nothing found'");
|
||||||
|
test.todo("Remove collections header when none found");
|
||||||
|
test.todo("Remove posts header when none found");
|
||||||
|
|
||||||
|
test.todo("Filter by tag works");
|
||||||
|
test.todo("Filter by content type");
|
||||||
|
test.todo("Sort by date works");
|
||||||
|
test.todo("Filter by author works");
|
||||||
|
|
||||||
|
// Changing pages to page 2 shows second page of results
|
||||||
|
test.todo("Pagination works");
|
||||||
|
|
||||||
|
// Search page, sort order, etc
|
||||||
|
test.todo("Make sure that initial search props are not thrown away");
|
||||||
|
});
|
||||||
@@ -48,7 +48,7 @@ interface SearchPageProps {
|
|||||||
|
|
||||||
const MAX_POSTS_PER_PAGE = 6;
|
const MAX_POSTS_PER_PAGE = 6;
|
||||||
|
|
||||||
interface ServerReturnType {
|
export interface ServerReturnType {
|
||||||
posts: PostInfo[];
|
posts: PostInfo[];
|
||||||
totalPosts: number;
|
totalPosts: number;
|
||||||
collections: ExtendedCollectionInfo[];
|
collections: ExtendedCollectionInfo[];
|
||||||
@@ -97,6 +97,7 @@ function SearchPageBase({ unicornProfilePicMap }: SearchPageProps) {
|
|||||||
|
|
||||||
return fetch(`/api/search?query=${debouncedSearch}`, {
|
return fetch(`/api/search?query=${debouncedSearch}`, {
|
||||||
signal: signal,
|
signal: signal,
|
||||||
|
method: "GET",
|
||||||
}).then((res) => res.json() as Promise<ServerReturnType>);
|
}).then((res) => res.json() as Promise<ServerReturnType>);
|
||||||
},
|
},
|
||||||
queryKey: ["search", debouncedSearch],
|
queryKey: ["search", debouncedSearch],
|
||||||
@@ -353,7 +354,7 @@ function SearchPageBase({ unicornProfilePicMap }: SearchPageProps) {
|
|||||||
!isContentLoading &&
|
!isContentLoading &&
|
||||||
showArticles &&
|
showArticles &&
|
||||||
Boolean(data.posts.length) && (
|
Boolean(data.posts.length) && (
|
||||||
<Fragment>
|
<div data-testid={"HI"}>
|
||||||
<SubHeader tag="h1" text="Articles" />
|
<SubHeader tag="h1" text="Articles" />
|
||||||
<PostCardGrid
|
<PostCardGrid
|
||||||
listAriaLabel={"List of search result posts"}
|
listAriaLabel={"List of search result posts"}
|
||||||
@@ -374,7 +375,7 @@ function SearchPageBase({ unicornProfilePicMap }: SearchPageProps) {
|
|||||||
return `${window.location.pathname}?${pageParams.toString()}`;
|
return `${window.location.pathname}?${pageParams.toString()}`;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</SearchTag>
|
</SearchTag>
|
||||||
|
|||||||
Reference in New Issue
Block a user