mirror of
https://github.com/LukeHagar/unicorn-utterances.git
synced 2025-12-06 04:21:55 +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
|
||||
// Learn more: https://github.com/testing-library/jest-dom
|
||||
require("whatwg-fetch");
|
||||
require("@testing-library/jest-dom/jest-globals");
|
||||
|
||||
global.plausible = null;
|
||||
|
||||
global.IntersectionObserver = class IntersectionObserver {
|
||||
constructor() {}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const { resolve } = require("path");
|
||||
require('whatwg-fetch');
|
||||
|
||||
// Add any custom config to be passed to Jest
|
||||
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",
|
||||
"epub": "tsx --tsconfig tsconfig.script.json build-scripts/generate-epubs.ts",
|
||||
"tsc": "tsc --noEmit && astro check",
|
||||
"test": "jest",
|
||||
"test": "set DEBUG_PRINT_LIMIT=9999999999 && jest",
|
||||
"prepare": "husky install && playwright install"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -51,6 +51,8 @@
|
||||
"@testing-library/dom": "^9.3.1",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/preact": "^3.2.3",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/hast": "^3.0.0",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/json5": "^2.2.0",
|
||||
"@types/node": "^20.5.0",
|
||||
@@ -85,6 +87,7 @@
|
||||
"json5": "^2.2.3",
|
||||
"junk": "^4.0.1",
|
||||
"lint-staged": "^14.0.0",
|
||||
"msw": "^1.2.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"plausible-tracker": "^0.3.8",
|
||||
"playwright": "^1.37.0",
|
||||
@@ -114,8 +117,8 @@
|
||||
"unist-util-replace-all-between": "^0.1.1",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"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": {
|
||||
"*.{js,ts,astro}": "eslint --cache --fix",
|
||||
|
||||
@@ -16,10 +16,8 @@ export const Picture = ({
|
||||
}: PictureProps) => {
|
||||
return (
|
||||
<picture class={`${className || ""}`}>
|
||||
{picture.sources.map((attrs) => (
|
||||
<source {...attrs} />
|
||||
))}
|
||||
<img {...(picture.image as any)} {...imgAttrs} alt={alt} />
|
||||
{picture?.sources.map((attrs) => <source {...attrs} />)}
|
||||
<img {...((picture?.image as any) ?? {})} {...imgAttrs} alt={alt} />
|
||||
</picture>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ export const FilterSectionItem = ({
|
||||
const props = {
|
||||
isSelected: selected,
|
||||
onChange: onChange,
|
||||
"aria-label": label,
|
||||
};
|
||||
|
||||
const state = useToggleState(props);
|
||||
|
||||
@@ -41,6 +41,7 @@ export const SearchTopbar = ({
|
||||
>
|
||||
<SearchInput
|
||||
id="search-bar"
|
||||
aria-label="Search"
|
||||
class={style.searchbar}
|
||||
usedInPreact={true}
|
||||
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;
|
||||
|
||||
interface ServerReturnType {
|
||||
export interface ServerReturnType {
|
||||
posts: PostInfo[];
|
||||
totalPosts: number;
|
||||
collections: ExtendedCollectionInfo[];
|
||||
@@ -97,6 +97,7 @@ function SearchPageBase({ unicornProfilePicMap }: SearchPageProps) {
|
||||
|
||||
return fetch(`/api/search?query=${debouncedSearch}`, {
|
||||
signal: signal,
|
||||
method: "GET",
|
||||
}).then((res) => res.json() as Promise<ServerReturnType>);
|
||||
},
|
||||
queryKey: ["search", debouncedSearch],
|
||||
@@ -353,7 +354,7 @@ function SearchPageBase({ unicornProfilePicMap }: SearchPageProps) {
|
||||
!isContentLoading &&
|
||||
showArticles &&
|
||||
Boolean(data.posts.length) && (
|
||||
<Fragment>
|
||||
<div data-testid={"HI"}>
|
||||
<SubHeader tag="h1" text="Articles" />
|
||||
<PostCardGrid
|
||||
listAriaLabel={"List of search result posts"}
|
||||
@@ -374,7 +375,7 @@ function SearchPageBase({ unicornProfilePicMap }: SearchPageProps) {
|
||||
return `${window.location.pathname}?${pageParams.toString()}`;
|
||||
}}
|
||||
/>
|
||||
</Fragment>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</SearchTag>
|
||||
|
||||
Reference in New Issue
Block a user