Skip to content

Commit

Permalink
Merge pull request #67 from slidr-app/improve-e2e-tests
Browse files Browse the repository at this point in the history
e2e test presentation preferences + improve tests
  • Loading branch information
codyzu authored Feb 15, 2024
2 parents e660220 + d7a1440 commit a399d1c
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 19 deletions.
7 changes: 4 additions & 3 deletions src/components/NoteEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export default function NoteEditor({
);

return (
<>
<div className="relative not-first:mt-8">
<li className="not-first:mt-8">
<div className="relative">
<div className="grid grid-cols-4 lt-sm:grid-cols-2">
{pageIndices.map((pageIndex, indicesIndex) => (
<img
Expand All @@ -41,6 +41,7 @@ export default function NoteEditor({
indicesIndex === 0 && 'grid-col-span-2 grid-row-span-2',
)}
src={pages[pageIndex]}
alt={`Slide ${pageIndex + 1}`}
/>
))}
</div>
Expand Down Expand Up @@ -90,6 +91,6 @@ export default function NoteEditor({
<Markdown remarkPlugins={[remarkGfm]}>{markdown}</Markdown>
</div>
</div>
</>
</li>
);
}
5 changes: 3 additions & 2 deletions src/components/PresentationPreferencesEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ export default function PresentationPreferencesEditor({
}}
/>
</div>
<div className="flex flex-col w-full">
<div id="speaker-notes">Speaker notes:</div>
<ul className="flex flex-col w-full" aria-labelledby="speaker-notes">
{notes.map((note, noteIndex) => (
<NoteEditor
key={note.pageIndices.toString()}
Expand Down Expand Up @@ -174,7 +175,7 @@ export default function PresentationPreferencesEditor({
}}
/>
))}
</div>
</ul>
<SaveIndicator saveState={saveState} />
</div>
);
Expand Down
6 changes: 5 additions & 1 deletion src/pages/error.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ test('displays an error when the presentation does not exist', async ({
);

expect(errorLogs.length).toBeGreaterThanOrEqual(1);
expect(errorLogs[0]).toMatch(
const filteredLogs = errorLogs.filter((log) =>
log.includes("Error: Presentation 'does-not-exist' does not exist"),
);
expect(filteredLogs.length).toBeGreaterThanOrEqual(1);
expect(filteredLogs[0]).toMatch(
"Error: Presentation 'does-not-exist' does not exist",
);
});
15 changes: 15 additions & 0 deletions src/pages/presentation.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,19 @@ test('can add and clear reactions', async ({coverage}, testInfo) => {
).toHaveCount(0, {timeout: 500});
});

test('generates a session id if not set', async ({
page,
// @ts-expect-error activate coverage
coverage,
}) => {
await page.goto(`/p/${presentationId}`);

await expect(page.getByRole('img', {name: 'Slide page 1'})).toBeVisible();

const pageUrl = new URL(page.url());

// Expect at least 1 character for the session id
expect(pageUrl.searchParams.get('session')).toMatch(/.+/);
});

// TODO test confetti, but how?
155 changes: 142 additions & 13 deletions src/pages/upload.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
import {test, expect} from '../test/login-fixture';
import {generateId} from '../test/id';

test('upload button appears after signing in', async ({page, loginPage}) => {
test.setTimeout(30_000);
await page.goto('/');
await expect(page.getByRole('button', {name: /upload/i})).not.toBeVisible();

await loginPage.goto();
await loginPage.signIn();
await loginPage.signInComplete();
await page.goto('/');
await expect(page.getByRole('button', {name: /upload/i})).toBeVisible({
timeout: 20_000,
});
});
test.describe.configure({mode: 'parallel'});

test('can upload and view presentation', async ({page, loginPage}) => {
test.setTimeout(60_000);

await page.goto('/');
await expect(page.getByRole('button', {name: /upload/i})).not.toBeVisible();

await loginPage.goto();
await loginPage.signIn();
await loginPage.signInComplete();
Expand Down Expand Up @@ -68,3 +59,141 @@ test('can upload and view presentation', async ({page, loginPage}) => {
await expect(page3).toBeVisible();
await expect(page3).toHaveScreenshot('page-3.png');
});

test('can edit presentation settings', async ({page, loginPage}) => {
test.setTimeout(60_000);

await loginPage.goto();
await loginPage.signIn();
await loginPage.signInComplete();

const presentationName = `e2e test - ${generateId()}`;
await page.goto('/');
await page.getByRole('button', {name: /upload/i}).click();
await page
.getByRole('button', {
name: /drag 'n' drop/i,
})
.locator('input')
.setInputFiles('./src/test/pdf/test.pdf');
await expect(page.getByText(/done/i)).toBeVisible({timeout: 20_000});
await page.getByLabel(/title/i).fill(presentationName);
await expect(page.getByText(/saving/i)).toBeVisible();
await expect(page.getByText(/saving/i)).not.toBeVisible();
await page.getByRole('button', {name: /slidr/i}).click();

// Find the presentation in the list
const presentation = page
.getByRole('list', {
name: /presentations/i,
})
.getByRole('listitem')
.filter({
has: page.getByText(presentationName),
})
.first();
await expect(presentation).toBeVisible();

// Can edit my presentation
const editButton = presentation.getByRole('button', {name: 'edit'});
await expect(editButton).toBeVisible();
await editButton.click();

await expect(page.getByText('Presentation Settings')).toBeVisible();
await expect(page.getByRole('img', {name: 'Slide 1'})).toBeVisible();
const titleTextBox = page.getByLabel('Title:');
await expect(titleTextBox).toBeVisible();
const editedPresentationName = `rename - ${generateId()}`;
await titleTextBox.fill(editedPresentationName);

// Add a list into slide 1 notes
const page1Notes = page
.getByRole('list', {name: /speaker notes/i})
.getByRole('listitem')
.filter({
has: page.getByText(/page 1/i),
})
.first();
await page1Notes.getByRole('textbox').fill('* abc\n* def');
await expect(page1Notes.getByRole('listitem').getByText('abc')).toBeVisible();
await expect(page1Notes.getByRole('listitem').getByText('def')).toBeVisible();

// Add a header into slide 2 notes
const page2Notes = page
.getByRole('list', {name: /speaker notes/i})
.getByRole('listitem')
.filter({
has: page.getByText(/page 2/i),
})
.first();
await page2Notes.getByRole('textbox').fill('# title');
await expect(page2Notes.getByRole('heading', {name: 'title'})).toBeVisible();

// Copy slide 3 into slide 2 notes
const page3Notes = page
.getByRole('list', {name: /speaker notes/i})
.getByRole('listitem')
.filter({
has: page.getByText(/page 3/i),
})
.first();
const copyPreviousButton = page3Notes
.getByRole('button')
.and(page3Notes.getByTitle('Copy previous'));
await expect(copyPreviousButton).toBeVisible();
await copyPreviousButton.click();
const pages2And3Notes = page
.getByRole('list', {name: /speaker notes/i})
.getByRole('listitem')
.filter({
has: page.getByText(/pages 2 - 3/i),
})
.first();
await expect(
pages2And3Notes.getByRole('img', {name: 'Slide 2'}),
).toBeVisible();
await expect(
pages2And3Notes.getByRole('img', {name: 'Slide 3'}),
).toBeVisible();

// Wait for the saving to be done
await expect(page.getByText(/saving/i)).toBeVisible();
await expect(page.getByText(/saving/i)).not.toBeVisible();

// Find the presentation in the list
await page.getByRole('button', {name: /slidr/i}).click();
const editedPresentation = page
.getByRole('list', {
name: /presentations/i,
})
.getByRole('listitem')
.filter({
has: page.getByText(editedPresentationName),
})
.first();
await expect(editedPresentation).toBeVisible();

// Can edit my presentation
const presentButton = editedPresentation.getByRole('button', {
name: 'present',
});
await expect(presentButton).toBeVisible();
await presentButton.click();

const speakerButton = page.getByRole('button', {name: 'speaker'});
const popupPromise = page.waitForEvent('popup');
await speakerButton.click();
const popup = await popupPromise;
await popup.waitForLoadState();

await expect(popup.getByText('Slide: 1')).toBeVisible();
await expect(popup.getByRole('listitem').getByText('abc')).toBeVisible();

await popup.getByRole('button', {name: 'next'}).click();
await expect(popup.getByText('Slide: 2')).toBeVisible();
await expect(popup.getByRole('heading', {name: 'title'})).toBeVisible();

await popup.getByRole('button', {name: 'next'}).click();
await expect(popup.getByText('Slide: 3')).toBeVisible();
await expect(popup.getByRole('heading', {name: 'title'})).toBeVisible();
});
3 changes: 3 additions & 0 deletions src/pages/viewer.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ test('can share with share buttons', async ({
}) => {
await page.goto(`/v/${presentationId}?slide=2`);

// Wait for the presentation to load
await expect(page.getByRole('img', {name: 'Slide page 2'})).toBeVisible();

// Twitter
const tweetButton = page.getByRole('link', {name: 'tweet'});

Expand Down
14 changes: 14 additions & 0 deletions uno.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ export default defineConfig({
presets: [
presetUno(),
presetIcons({
// Normally we don't have to load iconify collections.
// However, when running playwright tests from vscode, the icons don't get loaded.
// Loading them explicitly seems to fix the problem.
collections: {
tabler: async () =>
import('@iconify-json/tabler/icons.json').then((i) => i.default),
// @ts-expect-error the type is being picked up from this json file, maybe it's too large?
'fluent-emoji-flat': async () =>
import('@iconify-json/fluent-emoji-flat/icons.json').then(
(i) => i.default,
),
'line-md': async () =>
import('@iconify-json/line-md/icons.json').then((i) => i.default),
},
extraProperties: {
display: 'inline-block',
'vertical-align': 'middle',
Expand Down

0 comments on commit a399d1c

Please sign in to comment.