Skip to content

Commit

Permalink
cleanup test
Browse files Browse the repository at this point in the history
  • Loading branch information
codyzu committed Jul 23, 2023
1 parent 227968a commit 1efca66
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 97 deletions.
1 change: 1 addition & 0 deletions __mocks__/firebase/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export async function uploadBytes(
return uploadBytesOriginal(
ref,
// Firebase/storage expects a Buffer when running in node (i.e. tests)
// It does not seem to work with any other type when running in node
// If we pass a "MagicFile" automatically convert it to a Buffer
data instanceof MagicFile ? data.getBuffer() : data,
metadata,
Expand Down
11 changes: 7 additions & 4 deletions src/components/pdf/__mocks__/Pdf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import OriginalPdf from '../Pdf';
import {type MagicFile} from '../../../test/magic-file';

const pages = await Promise.all([
readFile('./test1.webp'),
readFile('./test2.webp'),
readFile('./test3.webp'),
readFile('./src/test/pdf/page1.webp'),
readFile('./src/test/pdf/page2.webp'),
readFile('./src/test/pdf/page3.webp'),
]);

export default function Pdf({
Expand All @@ -20,13 +20,16 @@ export default function Pdf({
onPageRendered: (canvas: HTMLCanvasElement) => void;
pageIndex: number;
}) {
console.log('overriding PDF');
return (
<OriginalPdf
// Rendering with jsdom seems to only work when using a data URI
// Convert the uploaded file to a data URI for the test
file={(file as MagicFile).getDataUri()}
pageIndex={pageIndex}
onSetPageCount={onSetPageCount}
onPageRendered={(canvas) => {
// The mocked canvas toBlob doesn't do anything by default
// In our tests, we will call the callback with pre-rendered page image
vi.mocked(canvas.toBlob).mockImplementation(
(callback: BlobCallback) => {
callback(pages[pageIndex] as unknown as Blob);
Expand Down
63 changes: 44 additions & 19 deletions src/pages/Home.Test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ beforeAll(async () => {
await setDoc(doc(firestore, 'presentations', 'home-p1'), {
uid: cred.user.uid,
created: now,
username: 'test user',
username: 'home user',
pages: [],
notes: [],
title: 'home1',
Expand All @@ -51,7 +51,7 @@ beforeAll(async () => {
await setDoc(doc(firestore, 'presentations', 'home-p2'), {
uid: cred.user.uid,
created: now,
username: 'test user',
username: 'home user',
pages: ['img1.jpg', 'img2.jpg', 'img3.jpg'],
notes: [],
title: 'home2',
Expand All @@ -67,7 +67,7 @@ beforeAll(async () => {
await setDoc(doc(firestore, 'presentations', 'home-p3'), {
uid: cred.user.uid,
created: now,
username: 'test user',
username: 'home user',
pages: ['img1.jpg', 'img2.jpg', 'img3.jpg'],
notes: [],
title: 'home3',
Expand All @@ -91,14 +91,25 @@ describe('Home view', () => {
const presentationList = await screen.findByRole('list', {
name: /presentations/i,
});
const presentations = await within(presentationList).findAllByRole(
const allPresentations = await within(presentationList).findAllByRole(
'listitem',
);

// Other tests may add presentations, so expect the length to be at least 3
expect(allPresentations).toHaveProperty('length');
expect(allPresentations.length).toBeGreaterThanOrEqual(3);

const presentations = allPresentations
.map((presentation) =>
within(presentation).queryByRole('link', {name: /home user/}),
)
.filter(Boolean) as HTMLElement[];

expect(presentations).toHaveLength(3);

await within(presentations[0]).findByRole('link', {name: /view.*home1/});
await within(presentations[1]).findByRole('link', {name: /view.*home2/});
await within(presentations[2]).findByRole('link', {name: /view.*home3/});
await within(presentations[0]).findByText(/home1/);
await within(presentations[1]).findByText(/home2/);
await within(presentations[2]).findByText(/home3/);
});

it('shows the edit button only on owned slideshows', async () => {
Expand All @@ -107,15 +118,29 @@ describe('Home view', () => {
const presentationList = await screen.findByRole('list', {
name: /presentations/i,
});
const presentations = await within(presentationList).findAllByRole(
const allPresentations = await within(presentationList).findAllByRole(
'listitem',
);

await within(presentations[0]).findByRole('button', {name: /edit/});
await within(presentations[1]).findByRole('button', {name: /edit/});
expect(
within(presentations[2]).queryByRole('button', {name: /edit/}),
).toBeNull();
const ownedPresentations = allPresentations.filter((presentation) =>
within(presentation).queryByText('home user'),
);

const notOwnedPresentations = allPresentations.filter((presentation) =>
within(presentation).queryByText('home user'),
);

for (const presentation of ownedPresentations) {
expect(
within(presentation).queryByRole('button', {name: /edit/}),
).not.toBeNull();
}

for (const presentation of notOwnedPresentations) {
expect(
within(presentation).queryAllByRole('button', {name: /edit/}),
).toBeNull();
}
});

it('shows the view button and present buttons on each', async () => {
Expand All @@ -128,11 +153,11 @@ describe('Home view', () => {
'listitem',
);

await within(presentations[0]).findByRole('button', {name: /view/});
await within(presentations[0]).findByRole('button', {name: /present/});
await within(presentations[1]).findByRole('button', {name: /view/});
await within(presentations[1]).findByRole('button', {name: /present/});
await within(presentations[2]).findByRole('button', {name: /view/});
await within(presentations[2]).findByRole('button', {name: /present/});
expect(presentations.length).toBeGreaterThanOrEqual(3);

for (const presentation of presentations) {
expect(within(presentation).queryByRole('button', {name: /view/}));
expect(within(presentation).queryByRole('button', {name: /present/}));
}
});
});
75 changes: 13 additions & 62 deletions src/pages/Upload.Test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ beforeAll(async () => {
});

describe('Presentation view', () => {
it.only('renders and uploads the test presentation', async () => {
it('renders and uploads the test presentation', async () => {
const userContext = {
user: {email: cred.user.email ?? undefined, uid: cred.user.uid},
setUser: () => undefined,
Expand Down Expand Up @@ -78,70 +78,21 @@ describe('Presentation view', () => {

// The magic file can be transformed to dataURI for react-pdf.Document
// and a buffer for firebase/storage.uploadBytes (which does not support File objects when running in node)
const pdfFile = new MagicFile(await readFile('./test.pdf'), 'test.pdf', {
type: 'application/pdf',
const pdfFile = new MagicFile(
await readFile('./src/test/pdf/test.pdf'),
'test.pdf',
{
type: 'application/pdf',
},
);

// Jsdom mocks getComputedStyle, we need to return something so that the watermarking works
vi.spyOn(window, 'getComputedStyle').mockImplementation(() => {
const styles = new CSSStyleDeclaration();
return styles;
});

await userEvent.upload(uploaderInput!, pdfFile);
await waitFor(() => screen.getByText(/done/i));
});

it('can navigate to the next slide with toolbar', async () => {
renderRoute('/p/presentation-1');
await screen.findByRole('img', {name: 'Slide page 1'});
const next = screen.getByRole('button', {name: 'next'});
await userEvent.click(next);
const slide2 = await screen.findByRole('img', {name: 'Slide page 2'});
expect(slide2).toHaveAttribute('src', 'img2.jpg');
});

it('can navigate to the last slide with toolbar', async () => {
renderRoute('/p/presentation-1');
await screen.findByRole('img', {name: 'Slide page 1'});
const end = screen.getByRole('button', {name: 'end'});
await userEvent.click(end);
const slide2 = await screen.findByRole('img', {name: 'Slide page 3'});
expect(slide2).toHaveAttribute('src', 'img3.jpg');
});

it('can navigate to the next slide by clicking on the image', async () => {
renderRoute('/p/presentation-1');
const slide1 = await screen.findByRole('img', {name: 'Slide page 1'});
await userEvent.click(slide1);
const slide2 = await screen.findByRole('img', {name: 'Slide page 2'});
expect(slide2).toHaveAttribute('src', 'img2.jpg');
});

it('can navigate to the last slide with toolbar', async () => {
renderRoute('/p/presentation-1');
await screen.findByRole('img', {name: 'Slide page 1'});
const end = screen.getByRole('button', {name: 'end'});
await userEvent.click(end);
const slide2 = await screen.findByRole('img', {name: 'Slide page 3'});
expect(slide2).toHaveAttribute('src', 'img3.jpg');
});

it('can read the slide index from the search params', async () => {
renderRoute('/p/presentation-1?slide=2');
const slide2 = await screen.findByRole('img', {name: 'Slide page 2'});
expect(slide2).toHaveAttribute('src', 'img2.jpg');
});

it('can navigate to the previous slide with toolbar', async () => {
renderRoute('/p/presentation-1?slide=2');
await screen.findByRole('img', {name: 'Slide page 2'});
const previous = await screen.findByRole('button', {name: 'previous'});
await userEvent.click(previous);
const slide1 = await screen.findByRole('img', {name: 'Slide page 1'});
expect(slide1).toHaveAttribute('src', 'img1.jpg');
});

it('can navigate to the first slide with toolbar', async () => {
renderRoute('/p/presentation-1?slide=3');
await screen.findByRole('img', {name: 'Slide page 3'});
const start = await screen.findByRole('button', {name: 'start'});
await userEvent.click(start);
const slide1 = await screen.findByRole('img', {name: 'Slide page 1'});
expect(slide1).toHaveAttribute('src', 'img1.jpg');
});
});
15 changes: 5 additions & 10 deletions src/pages/Upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@ import {UserContext, type UserDoc} from '../components/UserProvider';
import Loading from '../components/Loading';
import Pdf from '../components/pdf/Pdf';

if (!import.meta.env.VITEST) {
const src = new URL('pdfjs-dist/build/pdf.worker.js', import.meta.url);
pdfjs.GlobalWorkerOptions.workerSrc = src.toString();
}
const src = new URL('pdfjs-dist/build/pdf.worker.js', import.meta.url);
pdfjs.GlobalWorkerOptions.workerSrc = src.toString();

const storage = getStorage(app);

Expand Down Expand Up @@ -129,20 +127,17 @@ export default function Upload() {
maxFiles: 1,
});

const canvasRef = useRef<HTMLCanvasElement>(null);

const [renderingDone, setRenderingDone] = useState(false);
const [pageBlob, setPageBlob] = useState<Blob>();
function pageRendered(canvas: HTMLCanvasElement) {
// Watermark rendered image
const ctx = canvas.getContext('2d');
if (!import.meta.env.VITEST && ctx) {
if (ctx) {
const rootStyles = window.getComputedStyle(
document.querySelector('#root')!,
);

const fontFamily = rootStyles.getPropertyValue('font-family');
// Const fontSize = rootStyles.getPropertyValue('font-size');

// The weight should match an already loaded font so that all watermarks have the same dimensions.
// Otherwise, the first page may not have the correctly scaled font.
Expand All @@ -154,12 +149,12 @@ export default function Upload() {

const textMetrics = ctx.measureText('slidr.app');
const x =
canvasRef.current!.width -
canvas.width -
20 -
textMetrics.actualBoundingBoxLeft -
textMetrics.actualBoundingBoxRight;
const y =
canvasRef.current!.height -
canvas.height -
0 -
textMetrics.fontBoundingBoxAscent -
textMetrics.fontBoundingBoxDescent;
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 1 addition & 2 deletions src/test/setup.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// eslint-disable-next-line import/no-unassigned-import
import '@testing-library/jest-dom';
// eslint-disable-next-line import/no-unassigned-import
import 'blob-polyfill';
// PDF rendering and the upload page rely heavily on the canvas, it needs to be mocked
// eslint-disable-next-line import/no-unassigned-import
import 'vitest-canvas-mock';
// Import '@testing-library/jest-dom/extend-expect';
Expand Down

0 comments on commit 1efca66

Please sign in to comment.