Skip to content

Commit

Permalink
add integration tests for speaker + audience views
Browse files Browse the repository at this point in the history
  • Loading branch information
codyzu committed Jul 11, 2023
1 parent 0ad17bf commit 8981e00
Show file tree
Hide file tree
Showing 7 changed files with 371 additions and 10 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"test": "firebase emulators:exec --project demo-test --only auth,firestore,storage vitest",
"test:once": "firebase emulators:exec --project demo-test --only auth,firestore,storage 'vitest --run'",
"test:ui": "firebase emulators:exec --project demo-test --only auth,firestore,storage 'vitest --ui'",
"coverage": "firebase emulators:exec --project demo-test --only auth,firestore,storage 'vitest run --coverage'"
"coverage": "firebase emulators:exec --project demo-test --only auth,firestore,storage 'vitest run --coverage'",
"emulators": "firebase emulators:start --project demo-test --only auth,firestore,storage"
},
"dependencies": {
"@unocss/reset": "^0.53.3",
Expand Down
9 changes: 9 additions & 0 deletions src/components/reactions/Reactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ import {type Reaction as ReactionType} from './use-reactions';
// https://eng.butter.us/awesome-floating-emoji-reactions-using-framer-motion-styled-components-and-lottie-36b9f479a9f9
// https://www.daily.co/blog/add-flying-emoji-reactions-to-a-custom-daily-video-call/

const reactionLabels: Record<string, string> = {
'i-fluent-emoji-flat-red-heart': 'love',
'i-fluent-emoji-flat-smiling-face': 'smile',
'i-fluent-emoji-flat-clapping-hands': 'applause',
'i-fluent-emoji-flat-exploding-head': 'mind blown',
};

function Reaction({
onReactionDone,
icon = 'i-fluent-emoji-flat-red-heart',
Expand Down Expand Up @@ -53,6 +60,8 @@ function Reaction({
return (
<div
ref={reactionRef}
aria-label={reactionLabels[icon] ?? 'unknown'}
role="figure"
style={{left: `var(--reaction-x-offset)`}}
className={clsx(
'animate-emoji absolute bottom--100px bottom--12 leading-none text-size-12 overflow-visible',
Expand Down
137 changes: 137 additions & 0 deletions src/pages/Audience.Test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import {connectStorageEmulator, getStorage} from 'firebase/storage';
import {connectAuthEmulator, signInAnonymously} from 'firebase/auth';
import {connectFirestoreEmulator, doc, setDoc} from 'firebase/firestore';
import {RouterProvider, createMemoryRouter} from 'react-router-dom';
import {app, auth, firestore} from '../firebase';
import {screen, userEvent, render, findByRole} from '../test/test-utils';
import Routes from '../Routes';

beforeAll(async () => {
const storage = getStorage(app);
connectAuthEmulator(auth, 'http://127.0.0.1:9099');
connectFirestoreEmulator(firestore, '127.0.0.1', 8080);
connectStorageEmulator(storage, '127.0.0.1', 9199);

// Use anonymous auth because email link is complicated to emulate
const cred = await signInAnonymously(auth);

await fetch(
'http://127.0.0.1:8080/emulator/v1/projects/demo-test/databases/(default)/documents/presentations/presentation-2',
{method: 'DELETE'},
);

await fetch(
'http://127.0.0.1:8080/emulator/v1/projects/demo-test/databases/(default)/documents/sessions/test',
{method: 'DELETE'},
);

await fetch(
'http://127.0.0.1:8080/emulator/v1/projects/demo-test/databases/(default)/documents/sessions/test/reactions',
{method: 'DELETE'},
);

// Add a single presentation to firestore
await setDoc(doc(firestore, 'presentations', 'presentation-2'), {
uid: cred.user.uid,
created: Date.now(),
username: 'test user',
pages: ['img1.jpg', 'img2.jpg', 'img3.jpg'],
notes: [],
title: 'test presentation',
});
});

describe('Audience view', () => {
it('synchronizes the current slide', async () => {
const presentationRouter = createMemoryRouter(Routes, {
initialEntries: ['/p/presentation-2?slide=1&session=test'],
});
const audienceRouter = createMemoryRouter(Routes, {
initialEntries: ['/i/presentation-2?slide=1&session=test'],
});

render(
<>
<div data-testid="presentation">
<RouterProvider router={presentationRouter} />
</div>
<div data-testid="audience">
<RouterProvider router={audienceRouter} />
</div>
</>,
);

const presentation = await screen.findByTestId('presentation');
const audience = await screen.findByTestId('audience');

await findByRole(audience, 'img', {name: 'Slide page 1'});
const next = await findByRole(presentation, 'button', {name: 'next'});
await userEvent.click(next);
const slide2 = await findByRole(audience, 'img', {name: 'Slide page 2'});
expect(slide2).toHaveAttribute('src', 'img2.jpg');
});

it('renders reactions on audience view', async () => {
const presentationRouter = createMemoryRouter(Routes, {
initialEntries: ['/p/presentation-2?slide=1&session=test'],
});
const audienceRouter = createMemoryRouter(Routes, {
initialEntries: ['/i/presentation-2?slide=1&session=test'],
});

render(
<>
<div data-testid="presentation">
<RouterProvider router={presentationRouter} />
</div>
<div data-testid="audience">
<RouterProvider router={audienceRouter} />
</div>
</>,
);

const presentation = await screen.findByTestId('presentation');
const audience = await screen.findByTestId('audience');

await findByRole(presentation, 'img', {name: 'Slide page 1'});

const love = await findByRole(audience, 'button', {name: 'love'});
await userEvent.click(love);

await findByRole(audience, 'figure', {
name: 'love',
});
});

it('renders reactions on presentation view', async () => {
const presentationRouter = createMemoryRouter(Routes, {
initialEntries: ['/p/presentation-2?slide=1&session=test'],
});
const audienceRouter = createMemoryRouter(Routes, {
initialEntries: ['/i/presentation-2?slide=1&session=test'],
});

render(
<>
<div data-testid="presentation">
<RouterProvider router={presentationRouter} />
</div>
<div data-testid="audience">
<RouterProvider router={audienceRouter} />
</div>
</>,
);

const presentation = await screen.findByTestId('presentation');
const audience = await screen.findByTestId('audience');

await findByRole(audience, 'img', {name: 'Slide page 1'});

const love = await findByRole(audience, 'button', {name: 'love'});
await userEvent.click(love);

await findByRole(presentation, 'figure', {
name: 'love',
});
});
});
36 changes: 28 additions & 8 deletions src/pages/Presentation.Test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ beforeAll(async () => {
// Use anonymous auth because email link is complicated to emulate
const cred = await signInAnonymously(auth);

// Clear firestore before we start
await fetch(
'http://127.0.0.1:8080/emulator/v1/projects/demo-test/databases/(default)/documents',
'http://127.0.0.1:8080/emulator/v1/projects/demo-test/databases/(default)/documents/presentations/presentation-1',
{method: 'DELETE'},
);

Expand Down Expand Up @@ -46,6 +45,15 @@ describe('Presentation view', () => {
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'});
Expand All @@ -54,6 +62,15 @@ describe('Presentation view', () => {
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'});
Expand All @@ -69,13 +86,16 @@ describe('Presentation view', () => {
expect(slide1).toHaveAttribute('src', 'img1.jpg');
});

// It.skip('should increment count on click', async () => {
// render(<Presentation />);
// void userEvent.click(screen.getByRole('button'));
// expect(await screen.findByText(/count is: 1/i)).toBeInTheDocument();
// });
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');
});

// it.skip('uses flexbox in app header', async () => {
// It.skip('uses flexbox in app header', async () => {
// render(<Presentation />);
// const element = screen.getByRole('banner');
// expect(element.className).toEqual('App-header');
Expand Down
Loading

0 comments on commit 8981e00

Please sign in to comment.