Skip to content

Scanner · E2E tests

3 modules · 29 exports · 0 repositories · 14 e2e tests · 1 route

  • NN-ZAP
  • Users a test creates itself are coloured per test
  • Arrange data setup
  • Act what the user does
  • Assert what is expected

Scanner coach mode

opens a scanned file's game in coach mode and plays it again after a reload

Renders/scanner

LOLogged out
Scanner page
Act
  1. Open the scanner page
    await scanner.goto();
  2. Scan file VOD_PATH
    await scanner.scanFile(VOD_PATH);
  3. Click open in coach mode link
    await scanner.locators.openInCoachModeLink.click();
Scanner coach page
Assert
  1. title is visible
    await expect(coach.locators.title).toBeVisible();
  2. games summary reads “1 of 1 games”
    await expect(coach.locators.gamesSummary).toHaveText("1 of 1 games");
  3. no events is visible
    await expect(coach.locators.noEvents).toBeVisible();
  4. () => coach.currentTime() be greater than or equal INTRO_SECONDS.from
    await expect
    	.poll(() => coach.currentTime())
    	.toBeGreaterThanOrEqual(INTRO_SECONDS.from);
Act
  1. Current time → startedAt
    const startedAt = await coach.currentTime();
Assert
  1. startedAt is less than INTRO_SECONDS.to
    expect(startedAt).toBeLessThan(INTRO_SECONDS.to);
Act
  1. Click forward seconds button
    await coach.locators.forwardSecondsButton.click();
Assert
  1. () => coach.currentTime() is more than startedAt + 4
    await expect.poll(() => coach.currentTime()).toBeGreaterThan(startedAt + 4);
Act
  1. Reload
    await coach.reload();
Assert
  1. reopen prompt is visible
    await expect(coach.locators.reopenPrompt).toBeVisible();
Act
  1. Open file VOD_PATH
    await coach.openFile(VOD_PATH);
Assert
  1. play button is visible
    await expect(coach.locators.playButton).toBeVisible();
Source
test("opens a scanned file's game in coach mode and plays it again after a reload", async ({
	page,
}) => {
	const scanner = new ScannerPage(page);
	await scanner.goto();
	await scanner.scanFile(VOD_PATH);

	await scanner.locators.openInCoachModeLink.click();

	const coach = new ScannerCoachPage(page);
	await expect(coach.locators.title).toBeVisible();
	await expect(coach.locators.gamesSummary).toHaveText("1 of 1 games");
	await expect(coach.locators.noEvents).toBeVisible();
	await expect
		.poll(() => coach.currentTime())
		.toBeGreaterThanOrEqual(INTRO_SECONDS.from);
	const startedAt = await coach.currentTime();
	expect(startedAt).toBeLessThan(INTRO_SECONDS.to);

	await coach.locators.forwardSecondsButton.click();
	await expect.poll(() => coach.currentTime()).toBeGreaterThan(startedAt + 4);

	await coach.reload();
	await expect(coach.locators.reopenPrompt).toBeVisible();
	await coach.openFile(VOD_PATH);
	await expect(coach.locators.playButton).toBeVisible();
})

steps through a saved scan's coach events and coaches a cast from either team

Renders/scanner

LOLogged out
Scanner coach page
Act
  1. Open the scanner coach page “scanner-coach-vod.webm”
    await coach.goto(COACH_VOD_NAME);
Assert
  1. not found is visible
    await expect(coach.locators.notFound).toBeVisible();
Act
  1. Seed scan name “scanner-coach-vod.webm” duration 315 events COACH_EVENTS
    await coach.seedScan({
    	name: COACH_VOD_NAME,
    	duration: COACH_VOD_DURATION,
    	events: COACH_EVENTS,
    });
  2. Reload
    await coach.reload();
Assert
  1. reopen prompt is visible
    await expect(coach.locators.reopenPrompt).toBeVisible();
Act
  1. Open file COACH_VOD_PATH
    await coach.openFile(COACH_VOD_PATH);
Assert
  1. games summary reads “2 of 2 games”
    await expect(coach.locators.gamesSummary).toHaveText("2 of 2 games");
  2. event entry “Special used” is visible
    await expect(coach.eventEntry("Special used")).toBeVisible();
Act
  1. Select category “Zone”
    await coach.selectCategory("Zone");
Arrange
  1. [first, second] = [ coach.locators.eventEntries.nth(0), coach.locators.eventEntries.nth(1), ]
    const [first, second] = [
    	coach.locators.eventEntries.nth(0),
    	coach.locators.eventEntries.nth(1),
    ];
Act
  1. Click next event button
    await coach.locators.nextEventButton.click();
Assert
  1. first has “aria-current” = “true”
    await expect(first).toHaveAttribute("aria-current", "true");
  2. async () => Math.floor(await coach.currentTime()) is coach.entryPosition(first)
    await expect
    	.poll(async () => Math.floor(await coach.currentTime()))
    	.toBe(await coach.entryPosition(first));
Act
  1. Click next event button
    await coach.locators.nextEventButton.click();
Assert
  1. second has “aria-current” = “true”
    await expect(second).toHaveAttribute("aria-current", "true");
  2. async () => Math.floor(await coach.currentTime()) is coach.entryPosition(second)
    await expect
    	.poll(async () => Math.floor(await coach.currentTime()))
    	.toBe(await coach.entryPosition(second));
Act
  1. Filter by mode “Rainmaker”
    await coach.filterByMode("Rainmaker");
Assert
  1. games summary reads “1 of 2 games”
    await expect(coach.locators.gamesSummary).toHaveText("1 of 2 games");
Act
  1. Click play button
    await coach.locators.playButton.click();
Assert
  1. () => coach.currentTime() be greater than or equal 215
    await expect
    	.poll(() => coach.currentTime())
    	.toBeGreaterThanOrEqual(CAST_GAME_STARTS_AT);
  2. cast picker is visible
    await expect(coach.locators.castPicker).toBeVisible();
Act
  1. Select category “Opening”
    await coach.selectCategory("Opening");
Assert
  1. event entry “Opening lost” is visible
    await expect(coach.eventEntry("Opening lost")).toBeVisible();
Act
  1. Click cast team game 2 team “Bravo”
    await coach.castTeam({ game: 2, team: "Bravo" }).click();
Assert
  1. event entry “Opening won” is visible
    await expect(coach.eventEntry("Opening won")).toBeVisible();
  2. event entry “Opening lost” count is 0
    await expect(coach.eventEntry("Opening lost")).toHaveCount(0);
Source
test("steps through a saved scan's coach events and coaches a cast from either team", async ({
	page,
}) => {
	const coach = new ScannerCoachPage(page);
	await coach.goto(COACH_VOD_NAME);
	await expect(coach.locators.notFound).toBeVisible();

	await coach.seedScan({
		name: COACH_VOD_NAME,
		duration: COACH_VOD_DURATION,
		events: COACH_EVENTS,
	});
	await coach.reload();
	await expect(coach.locators.reopenPrompt).toBeVisible();
	await coach.openFile(COACH_VOD_PATH);

	await expect(coach.locators.gamesSummary).toHaveText("2 of 2 games");
	await expect(coach.eventEntry("Special used")).toBeVisible();

	await coach.selectCategory("Zone");
	const [first, second] = [
		coach.locators.eventEntries.nth(0),
		coach.locators.eventEntries.nth(1),
	];
	await coach.locators.nextEventButton.click();
	await expect(first).toHaveAttribute("aria-current", "true");
	await expect
		.poll(async () => Math.floor(await coach.currentTime()))
		.toBe(await coach.entryPosition(first));

	await coach.locators.nextEventButton.click();
	await expect(second).toHaveAttribute("aria-current", "true");
	await expect
		.poll(async () => Math.floor(await coach.currentTime()))
		.toBe(await coach.entryPosition(second));

	await coach.filterByMode("Rainmaker");
	await expect(coach.locators.gamesSummary).toHaveText("1 of 2 games");
	await coach.locators.playButton.click();
	await expect
		.poll(() => coach.currentTime())
		.toBeGreaterThanOrEqual(CAST_GAME_STARTS_AT);

	await expect(coach.locators.castPicker).toBeVisible();
	await coach.selectCategory("Opening");
	await expect(coach.eventEntry("Opening lost")).toBeVisible();

	await coach.castTeam({ game: 2, team: "Bravo" }).click();
	await expect(coach.eventEntry("Opening won")).toBeVisible();
	await expect(coach.eventEntry("Opening lost")).toHaveCount(0);
})

Scanner live capture

captures a game from a capture card into a session

Renders/scanner

LOLogged out
Fake capture card
Act
  1. Install
    await capture.install();
Scanner page
Act
  1. Open the scanner page
    await scanner.goto();
  2. Start capture scanner
    await startCapture(scanner);
  3. Play fixture game scanner, capture
    await playFixtureGame(scanner, capture);
Assert
  1. the scanner page captured game
    await expectCapturedGame(scanner);
Act
  1. Click stop button
    await scanner.locators.stopButton.click();
  2. Click first live session links
    await scanner.locators.liveSessionLinks.first().click();
Assert
  1. the scanner page captured game
    await expectCapturedGame(scanner);
Source
test("captures a game from a capture card into a session", async ({
	page,
}) => {
	const capture = new FakeCaptureCard(page);
	await capture.install();
	const scanner = new ScannerPage(page);
	await scanner.goto();

	await startCapture(scanner);
	await playFixtureGame(scanner, capture);
	await expectCapturedGame(scanner);

	await scanner.locators.stopButton.click();
	await scanner.locators.liveSessionLinks.first().click();

	await expectCapturedGame(scanner);
})

uploads a captured game, linking it to the SendouQ match it was played in

Renders/q/match/:id/scanner

Setup
Arrange
  1. Create players FIXTURE_GAME.winners → winners
    const winners = await createPlayers(factories, FIXTURE_GAME.winners);
  2. Create players FIXTURE_GAME.losers → losers
    const losers = await createPlayers(factories, FIXTURE_GAME.losers);
  3. Create SQ match alpha user ids winners.map((user) => user.id) bravo user ids losers.map((user) => user.id) map list SET_STAGE_IDS.map((stageId) => ({ mode: "SZ", stageId, source: "BOTH", })) is concluded true → match
    const match = await factories.SQMatchFactory.create(
    	{
    		alphaUserIds: winners.map((user) => user.id),
    		bravoUserIds: losers.map((user) => user.id),
    		mapList: SET_STAGE_IDS.map((stageId) => ({
    			mode: "SZ",
    			stageId,
    			source: "BOTH",
    		})),
    	},
    	{ isConcluded: true },
    );
  4. povUser = winners[FIXTURE_GAME.winners.indexOf(FIXTURE_GAME.pov.name)]
    const povUser =
    	winners[FIXTURE_GAME.winners.indexOf(FIXTURE_GAME.pov.name)];
PUpov user
Fake capture card
Act
  1. Install
    await capture.install();
Scanner page
Act
  1. Open the scanner page
    await scanner.goto();
  2. Start capture scanner
    await startCapture(scanner);
  3. Play fixture game scanner, capture
    await playFixtureGame(scanner, capture);
  4. Click upload status button “Uploaded to sendou.ink”
    await scanner.uploadStatusButton("Uploaded to sendou.ink").click();
Assert
  1. open match link has “href” = new RegExp(`/q/match/${match.id}$`)
    await expect(scanner.locators.openMatchLink).toHaveAttribute(
    	"href",
    	new RegExp(`/q/match/${match.id}$`),
    );
SendouQ match page
Act
  1. Open the SendouQ match page match.id, “result”
    await matchPage.goto(match.id, "result");
  2. Click first scoreboard details button
    await matchPage.locators.scoreboardDetailsButton.first().click();
Assert
  1. scoreboard row FIXTURE_GAME.pov.name contains `${FIXTURE_GAME.pov.ka}${FIXTURE_GAME.pov.d}${FIXTURE_GAME.pov.s}`
    await expect(matchPage.scoreboardRow(FIXTURE_GAME.pov.name)).toContainText(
    	`${FIXTURE_GAME.pov.ka}${FIXTURE_GAME.pov.d}${FIXTURE_GAME.pov.s}`,
    );
Source
test("uploads a captured game, linking it to the SendouQ match it was played in", async ({
	page,
	factories,
}) => {
	const winners = await createPlayers(factories, FIXTURE_GAME.winners);
	const losers = await createPlayers(factories, FIXTURE_GAME.losers);
	const match = await factories.SQMatchFactory.create(
		{
			alphaUserIds: winners.map((user) => user.id),
			bravoUserIds: losers.map((user) => user.id),
			mapList: SET_STAGE_IDS.map((stageId) => ({
				mode: "SZ",
				stageId,
				source: "BOTH",
			})),
		},
		{ isConcluded: true },
	);
	const povUser =
		winners[FIXTURE_GAME.winners.indexOf(FIXTURE_GAME.pov.name)];

	await impersonate(page, povUser.id);
	const capture = new FakeCaptureCard(page);
	await capture.install();
	const scanner = new ScannerPage(page);
	await scanner.goto();

	await startCapture(scanner);
	await playFixtureGame(scanner, capture);

	await scanner.uploadStatusButton("Uploaded to sendou.ink").click();
	await expect(scanner.locators.openMatchLink).toHaveAttribute(
		"href",
		new RegExp(`/q/match/${match.id}$`),
	);

	const matchPage = new SendouQMatchPage(page);
	await matchPage.goto(match.id, "result");
	await matchPage.locators.scoreboardDetailsButton.first().click();
	await expect(matchPage.scoreboardRow(FIXTURE_GAME.pov.name)).toContainText(
		`${FIXTURE_GAME.pov.ka}${FIXTURE_GAME.pov.d}${FIXTURE_GAME.pov.s}`,
	);
})

refuses to capture in a second tab while one is capturing

Renders/scanner

LOLogged out
Fake capture card
Act
  1. Install
    await capture.install();
Scanner page
Act
  1. Open the scanner page
    await scanner.goto();
  2. Start capture scanner
    await startCapture(scanner);
  3. New page → secondTab
    const secondTab = await context.newPage();
Fake capture card
Act
  1. Install
    await new FakeCaptureCard(secondTab).install();
Scanner page
Act
  1. Open the scanner page
    await secondScanner.goto();
  2. Click start capture button
    await secondScanner.locators.startCaptureButton.click();
Assert
  1. another tab capturing error is visible
    await expect(secondScanner.locators.anotherTabCapturingError).toBeVisible();
Source
test("refuses to capture in a second tab while one is capturing", async ({
	page,
	context,
}) => {
	const capture = new FakeCaptureCard(page);
	await capture.install();
	const scanner = new ScannerPage(page);
	await scanner.goto();
	await startCapture(scanner);

	const secondTab = await context.newPage();
	await new FakeCaptureCard(secondTab).install();
	const secondScanner = new ScannerPage(secondTab);
	await secondScanner.goto();
	await secondScanner.locators.startCaptureButton.click();

	await expect(secondScanner.locators.anotherTabCapturingError).toBeVisible();
})

tells when no capture card is connected

Renders/scanner

LOLogged out
Fake capture card
Act
  1. Install kind “webcam”
    await new FakeCaptureCard(page).install({ kind: "webcam" });
Scanner page
Act
  1. Open the scanner page
    await scanner.goto();
  2. Click start capture button
    await scanner.locators.startCaptureButton.click();
Assert
  1. no capture source error is visible
    await expect(scanner.locators.noCaptureSourceError).toBeVisible();
Source
test("tells when no capture card is connected", async ({ page }) => {
	await new FakeCaptureCard(page).install({ kind: "webcam" });
	const scanner = new ScannerPage(page);
	await scanner.goto();

	await scanner.locators.startCaptureButton.click();

	await expect(scanner.locators.noCaptureSourceError).toBeVisible();
})

Scanner

reads a screenshot in the debug view

Renders/scanner

LOLogged out
Scanner page
Act
  1. Open the scanner page
    await scanner.goto();
  2. Choose file SCOREBOARD_SCREENSHOT_PATH
    await scanner.chooseFile(SCOREBOARD_SCREENSHOT_PATH);
Assert
  1. main contains `scores [85,82] · ${GAME.lobby} · ${GAME.mode} · ${GAME.stage}`
    await expect(scanner.locators.main).toContainText(
    	`scores [85,82] · ${GAME.lobby} · ${GAME.mode} · ${GAME.stage}`,
    	{ timeout: SCAN_TIMEOUT },
    );
Source
test("reads a screenshot in the debug view", async ({ page }) => {
	const scanner = new ScannerPage(page);
	await scanner.goto();

	await scanner.chooseFile(SCOREBOARD_SCREENSHOT_PATH);

	await expect(scanner.locators.main).toContainText(
		`scores [85,82] · ${GAME.lobby} · ${GAME.mode} · ${GAME.stage}`,
		{ timeout: SCAN_TIMEOUT },
	);
})

scans a file into a match card that survives a reload

Renders/scanner

LOLogged out
Act
  1. Scan vod scanner
    await scanVod(scanner);
Assert
  1. the scanner page scanned game
    await expectScannedGame(scanner);
Scanner page
Act
  1. Click show details button
    await scanner.locators.showDetailsButton.click();
Assert
  1. For each player of ...GAME.winners, ...GAME.losers
    for (const player of [...GAME.winners, ...GAME.losers]) {
    	await expect(scanner.locators.main).toContainText(player);
    }
    1. main contains player
      await expect(scanner.locators.main).toContainText(player);
Act
  1. Reload
    await scanner.reload();
Assert
  1. the scanner page scanned game
    await expectScannedGame(scanner);
Act
  1. Open the scanner page
    await scanner.goto();
  2. Click session link “scanner-vod.webm”
    await scanner.sessionLink(VOD_NAME).click();
Assert
  1. the scanner page scanned game
    await expectScannedGame(scanner);
Source
test("scans a file into a match card that survives a reload", async ({
	page,
}) => {
	const scanner = new ScannerPage(page);
	await scanVod(scanner);

	await expectScannedGame(scanner);
	await scanner.locators.showDetailsButton.click();
	for (const player of [...GAME.winners, ...GAME.losers]) {
		await expect(scanner.locators.main).toContainText(player);
	}

	await scanner.reload();
	await expectScannedGame(scanner);

	await scanner.goto();
	await scanner.sessionLink(VOD_NAME).click();
	await expectScannedGame(scanner);
})

exports the scanned matches as CSV

Renders/scanner

LOLogged out
Act
  1. Scan vod scanner
    await scanVod(scanner);
Scanner page
Act
  1. Download matches csv → download
    const download = await scanner.downloadMatchesCsv();
Arrange
  1. [header, row, ...rest] = ( await fs.readFile(await download.path(), "utf-8") ) .trim() .split(/\r?\n/)…
    const [header, row, ...rest] = (
    	await fs.readFile(await download.path(), "utf-8")
    )
    	.trim()
    	.split(/\r?\n/)
    	.map((line) => line.split(","));
  2. game = Object.fromEntries(header.map((key, i) => [key, row[i]]))
    const game = Object.fromEntries(header.map((key, i) => [key, row[i]]));
Assert
  1. rest has 0 items
    expect(rest).toHaveLength(0);
  2. Number(game.t_seconds) be greater than or equal INTRO_SECONDS.from
    expect(Number(game.t_seconds)).toBeGreaterThanOrEqual(INTRO_SECONDS.from);
  3. Number(game.t_seconds) is less than INTRO_SECONDS.to
    expect(Number(game.t_seconds)).toBeLessThan(INTRO_SECONDS.to);
  4. game equals expect.objectContaining({ source: VOD_NAME, lobby: GAME.lobby, mode: GAME.mod…
    expect(game).toEqual(
    	expect.objectContaining({
    		source: VOD_NAME,
    		lobby: GAME.lobby,
    		mode: GAME.mode,
    		stage: GAME.stage,
    		result: "WIN",
    		score_for: "85",
    		score_against: "82",
    		weapon: GAME.pov.weapon,
    		ka: String(GAME.pov.ka),
    		d: String(GAME.pov.d),
    		s: String(GAME.pov.s),
    	}),
    );
Source
test("exports the scanned matches as CSV", async ({ page }) => {
	const scanner = new ScannerPage(page);
	await scanVod(scanner);

	const download = await scanner.downloadMatchesCsv();
	const [header, row, ...rest] = (
		await fs.readFile(await download.path(), "utf-8")
	)
		.trim()
		.split(/\r?\n/)
		.map((line) => line.split(","));

	const game = Object.fromEntries(header.map((key, i) => [key, row[i]]));

	expect(rest).toHaveLength(0);
	expect(Number(game.t_seconds)).toBeGreaterThanOrEqual(INTRO_SECONDS.from);
	expect(Number(game.t_seconds)).toBeLessThan(INTRO_SECONDS.to);
	expect(game).toEqual(
		expect.objectContaining({
			source: VOD_NAME,
			lobby: GAME.lobby,
			mode: GAME.mode,
			stage: GAME.stage,
			result: "WIN",
			score_for: "85",
			score_against: "82",
			weapon: GAME.pov.weapon,
			ka: String(GAME.pov.ka),
			d: String(GAME.pov.d),
			s: String(GAME.pov.s),
		}),
	);
})

downloads a scanned game's frames and reads as a zip

Renders/scanner

LOLogged out
Act
  1. Scan vod scanner
    await scanVod(scanner);
Scanner page
Act
  1. Click show details button
    await scanner.locators.showDetailsButton.click();
  2. Download game data → download
    const download = await scanner.downloadGameData();
Arrange
  1. files = unzipSync(await fs.readFile(await download.path()))
    const files = unzipSync(await fs.readFile(await download.path()));
  2. names = Object.keys(files)
    const names = Object.keys(files);
  3. events = JSON.parse(strFromU8(files["events.json"]!))
    const events = JSON.parse(strFromU8(files["events.json"]!));
Assert
  1. names contains “match.json”
    expect(names).toContain("match.json");
  2. names.some((name) => /^frames\/[^/]+-MapStart-[^/]+\/frame\.webp$/.test(name), ) is true
    expect(
    	names.some((name) =>
    		/^frames\/[^/]+-MapStart-[^/]+\/frame\.webp$/.test(name),
    	),
    ).toBe(true);
  3. events.some( (event: { type: string; frame?: string }) => event.type === "Map… is true
    expect(
    	events.some(
    		(event: { type: string; frame?: string }) =>
    			event.type === "MapStart" &&
    			files[`${event.frame}/expected.json`] !== undefined,
    	),
    ).toBe(true);
Source
test("downloads a scanned game's frames and reads as a zip", async ({
	page,
}) => {
	const scanner = new ScannerPage(page);
	await scanVod(scanner);
	await scanner.locators.showDetailsButton.click();

	const download = await scanner.downloadGameData();
	const files = unzipSync(await fs.readFile(await download.path()));
	const names = Object.keys(files);
	const events = JSON.parse(strFromU8(files["events.json"]!));

	expect(names).toContain("match.json");
	expect(
		names.some((name) =>
			/^frames\/[^/]+-MapStart-[^/]+\/frame\.webp$/.test(name),
		),
	).toBe(true);
	expect(
		events.some(
			(event: { type: string; frame?: string }) =>
				event.type === "MapStart" &&
				files[`${event.frame}/expected.json`] !== undefined,
		),
	).toBe(true);
})

prefills a new VoD from a scanned file

Renders/scanner/vods/new

Setup
Arrange
  1. Grant user N-ZAP roles “VIDEO_ADDER”
    await factories.UserFactory.grant(NZAP_TEST_ID, { roles: ["VIDEO_ADDER"] });
NN-ZAP
Act
  1. Scan vod scanner
    await scanVod(scanner);
Scanner page
Act
  1. Click add to vods button
    await scanner.locators.addToVodsButton.click();
New vod page
Act
  1. Match 0 → newVodMatch
    const newVodMatch = new NewVodPage(page).match(0);
Vod match fields
Assert
  1. starts at has value “/^0:0[3-8]$/”
    await expect(newVodMatch.locators.startsAt).toHaveValue(INTRO_TIMESTAMP);
  2. mode radio GAME.mode is checked
    await expect(newVodMatch.modeRadio(GAME.mode)).toBeChecked();
  3. stage select contains GAME.stage
    await expect(newVodMatch.locators.stageSelect).toContainText(GAME.stage);
  4. weapon select contains GAME.pov.weapon
    await expect(newVodMatch.locators.weaponSelect).toContainText(
    	GAME.pov.weapon,
    );
Source
test("prefills a new VoD from a scanned file", async ({
	page,
	factories,
}) => {
	await factories.UserFactory.grant(NZAP_TEST_ID, { roles: ["VIDEO_ADDER"] });
	await impersonate(page, NZAP_TEST_ID);
	const scanner = new ScannerPage(page);
	await scanVod(scanner);

	await scanner.locators.addToVodsButton.click();

	const newVodMatch = new NewVodPage(page).match(0);
	await expect(newVodMatch.locators.startsAt).toHaveValue(INTRO_TIMESTAMP);
	await expect(newVodMatch.modeRadio(GAME.mode)).toBeChecked();
	await expect(newVodMatch.locators.stageSelect).toContainText(GAME.stage);
	await expect(newVodMatch.locators.weaponSelect).toContainText(
		GAME.pov.weapon,
	);
})

asks a logged out visitor to log in before adding a scanned file to VoDs

Renders/scanner

LOLogged out
Act
  1. Scan vod scanner
    await scanVod(scanner);
Scanner page
Act
  1. Click add to vods logged out button
    await scanner.locators.addToVodsLoggedOutButton.click();
Log in popover
Assert
  1. log in button is visible
    await expect(new LogInPopover(page).locators.logInButton).toBeVisible();
Source
test("asks a logged out visitor to log in before adding a scanned file to VoDs", async ({
	page,
}) => {
	const scanner = new ScannerPage(page);
	await scanVod(scanner);

	await scanner.locators.addToVodsLoggedOutButton.click();

	await expect(new LogInPopover(page).locators.logInButton).toBeVisible();
})

deletes a scanned file

Renders/scanner

LOLogged out
Act
  1. Scan vod scanner
    await scanVod(scanner);
Scanner page
Act
  1. Delete scan
    await scanner.deleteScan();
Assert
  1. sessions heading is visible
    await expect(scanner.locators.sessionsHeading).toBeVisible();
  2. session link “scanner-vod.webm” is not visible
    await isNotVisible(scanner.sessionLink(VOD_NAME));
Source
test("deletes a scanned file", async ({ page }) => {
	const scanner = new ScannerPage(page);
	await scanVod(scanner);

	await scanner.deleteScan();

	await expect(scanner.locators.sessionsHeading).toBeVisible();
	await isNotVisible(scanner.sessionLink(VOD_NAME));
})

tells when an opened VoD is no longer saved

Renders/scanner

LOLogged out
Scanner page
Act
  1. Go to vod “missing.webm”
    await scanner.gotoVod("missing.webm");
Assert
  1. not found is visible
    await expect(scanner.locators.notFound).toBeVisible();
Source
test("tells when an opened VoD is no longer saved", async ({ page }) => {
	const scanner = new ScannerPage(page);
	await scanner.gotoVod("missing.webm");

	await expect(scanner.locators.notFound).toBeVisible();
})