Skip to content

Availability · E2E tests

10 modules · 45 exports · 1 repository · 8 e2e tests · 1 route

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

Events

filters between tabs and navigates to an event

Renders/events/to/:id/to/:id/info

Setup
Arrange
  1. startsAt = dateToDatabaseTimestamp(addHours(new Date(), 2))
    const startsAt = dateToDatabaseTimestamp(addHours(new Date(), 2));
  2. Create tournament author Sendou name “Joined Tournament” start times startsAt → joinedTournament
    const joinedTournament = await factories.TournamentFactory.create({
    	authorId: ADMIN_ID,
    	name: JOINED_TOURNAMENT_NAME,
    	startTimes: [startsAt],
    });
  3. Create tournament team tournament id joinedTournament.id member user ids N-ZAP
    await factories.TournamentTeamFactory.create({
    	tournamentId: joinedTournament.id,
    	memberUserIds: [NZAP_TEST_ID],
    });
  4. Create tournament author N-ZAP name “Organized Tournament” start times startsAt
    await factories.TournamentFactory.create({
    	authorId: NZAP_TEST_ID,
    	name: ORGANIZED_TOURNAMENT_NAME,
    	startTimes: [startsAt],
    });
  5. Create scrim post startsAt users user N-ZAP is owner 1
    await factories.ScrimPostFactory.create({
    	startsAt,
    	users: [{ userId: NZAP_TEST_ID, isOwner: 1 }],
    });
NN-ZAP
Events page
Act
  1. Open the events page
    await events.goto();
Assert
  1. title is visible
    await expect(events.locators.title).toBeVisible();
  2. the first non-empty category is shown by default

    event link “Joined Tournament” is visible
    await expect(events.eventLink(JOINED_TOURNAMENT_NAME)).toBeVisible();
Act
  1. Open view “scrims”
    await events.openView("scrims");
Assert
  1. event link “Looking for scrim” is visible
    await expect(events.eventLink("Looking for scrim")).toBeVisible();
Act
  1. Open view “saved”
    await events.openView("saved");
Assert
  1. empty category text is visible
    await expect(events.locators.emptyCategoryText).toBeVisible();
Act
  1. Open view “hosting”
    await events.openView("hosting");
  2. Event link “Organized Tournament” → hostedEvent
    const hostedEvent = events.eventLink(ORGANIZED_TOURNAMENT_NAME);
Assert
  1. hostedEvent is visible
    await expect(hostedEvent).toBeVisible();
Act
  1. Click hostedEvent
    await hostedEvent.click();
Assert
  1. the page URL is not /\/events/
    await expect(page).not.toHaveURL(/\/events/);
Source
test("filters between tabs and navigates to an event", async ({
	page,
	factories,
}) => {
	const startsAt = dateToDatabaseTimestamp(addHours(new Date(), 2));

	const joinedTournament = await factories.TournamentFactory.create({
		authorId: ADMIN_ID,
		name: JOINED_TOURNAMENT_NAME,
		startTimes: [startsAt],
	});
	await factories.TournamentTeamFactory.create({
		tournamentId: joinedTournament.id,
		memberUserIds: [NZAP_TEST_ID],
	});
	await factories.TournamentFactory.create({
		authorId: NZAP_TEST_ID,
		name: ORGANIZED_TOURNAMENT_NAME,
		startTimes: [startsAt],
	});
	await factories.ScrimPostFactory.create({
		startsAt,
		users: [{ userId: NZAP_TEST_ID, isOwner: 1 }],
	});

	await impersonate(page, NZAP_TEST_ID);

	const events = new EventsPage(page);
	await events.goto();

	await expect(events.locators.title).toBeVisible();

	// the first non-empty category is shown by default
	await expect(events.eventLink(JOINED_TOURNAMENT_NAME)).toBeVisible();

	await events.openView("scrims");
	await expect(events.eventLink("Looking for scrim")).toBeVisible();

	await events.openView("saved");
	await expect(events.locators.emptyCategoryText).toBeVisible();

	await events.openView("hosting");
	const hostedEvent = events.eventLink(ORGANIZED_TOURNAMENT_NAME);
	await expect(hostedEvent).toBeVisible();

	await hostedEvent.click();
	await expect(page).not.toHaveURL(/\/events/);
})

My schedule

saves a week, edits it and submits an empty week

Renders/events

SSendou
Events page
Act
  1. Open the events page
    await events.goto();
Assert
  1. week not filled marker “current” is visible
    await expect(events.weekNotFilledMarker("current")).toBeVisible();
Act
  1. Click day edit button 2
    await events.dayEditButton(WEDNESDAY).click();
  2. popover = day editor popover
    const popover = events.locators.dayEditorPopover;
  3. Fill popover.getByLabel("Start") with “18:00”
    await popover.getByLabel("Start").fill("18:00");
  4. Fill popover.getByLabel("End") with “22:00”
    await popover.getByLabel("End").fill("22:00");
  5. Fill popover.getByLabel("Note") with “Leaving early”
    await popover.getByLabel("Note").fill("Leaving early");
  6. Press Escape
    await page.keyboard.press("Escape");
Assert
  1. availability bars count is 1
    await expect(events.locators.availabilityBars).toHaveCount(1);
Act
  1. leaving the page with the unsaved week warns first

    Click link “Find an event to join on the calendar!”
    await page
    	.getByRole("link", { name: "Find an event to join on the calendar!" })
    	.click();
  2. Wait for
    await page.getByText("Unsaved changes").waitFor();
  3. Click button “Cancel”
    await page.getByRole("button", { name: "Cancel" }).click();
Assert
  1. the page URL is /\/events/
    await expect(page).toHaveURL(/\/events/);
Act
  1. Click save week button
    await events.locators.saveWeekButton.click();
Assert
  1. text “Availability saved” is in the page
    await expect(page.getByText("Availability saved")).toBeAttached();
Act
  1. Open the events page
    await events.goto();
Assert
  1. availability bars count is 1
    await expect(events.locators.availabilityBars).toHaveCount(1);
  2. week not filled marker “current” is not visible
    await isNotVisible(events.weekNotFilledMarker("current"));
  3. week not filled marker “next” is visible
    await expect(events.weekNotFilledMarker("next")).toBeVisible();
Act
  1. Click day edit button 2
    await events.dayEditButton(WEDNESDAY).click();
Assert
  1. popover.getByLabel("Note") has value “Leaving early”
    await expect(popover.getByLabel("Note")).toHaveValue("Leaving early");
Act
  1. deleting the only range commits instantly: the popover closes and the bar disappears without waiting for a popover close + save

    Click popover.getByRole("button", { name: "Delete" })
    await popover.getByRole("button", { name: "Delete" }).click();
Assert
  1. day editor popover is not visible
    await isNotVisible(events.locators.dayEditorPopover);
  2. availability bars is not visible
    await isNotVisible(events.locators.availabilityBars);
Act
  1. Click save week button
    await events.locators.saveWeekButton.click();
  2. Click element “confirm-button”
    await page.getByTestId("confirm-button").click();
Assert
  1. text “Availability saved” is in the page
    await expect(page.getByText("Availability saved")).toBeAttached();
Act
  1. an empty submitted week is "unavailable all week", not missing

    Open the events page
    await events.goto();
Assert
  1. availability bars is not visible
    await isNotVisible(events.locators.availabilityBars);
  2. week not filled marker “current” is not visible
    await isNotVisible(events.weekNotFilledMarker("current"));
Source
test("saves a week, edits it and submits an empty week", async ({ page }) => {
	await impersonate(page, ADMIN_ID);
	await setTimezoneCookie(page);

	const events = new EventsPage(page);
	await events.goto();

	await expect(events.weekNotFilledMarker("current")).toBeVisible();

	await events.dayEditButton(WEDNESDAY).click();
	const popover = events.locators.dayEditorPopover;
	await popover.getByLabel("Start").fill("18:00");
	await popover.getByLabel("End").fill("22:00");
	await popover.getByLabel("Note").fill("Leaving early");
	await page.keyboard.press("Escape");

	await expect(events.locators.availabilityBars).toHaveCount(1);

	// leaving the page with the unsaved week warns first
	await page
		.getByRole("link", { name: "Find an event to join on the calendar!" })
		.click();
	await page.getByText("Unsaved changes").waitFor();
	await page.getByRole("button", { name: "Cancel" }).click();
	await expect(page).toHaveURL(/\/events/);

	await events.locators.saveWeekButton.click();
	await expect(page.getByText("Availability saved")).toBeAttached();

	await events.goto();
	await expect(events.locators.availabilityBars).toHaveCount(1);
	await isNotVisible(events.weekNotFilledMarker("current"));
	await expect(events.weekNotFilledMarker("next")).toBeVisible();

	await events.dayEditButton(WEDNESDAY).click();
	await expect(popover.getByLabel("Note")).toHaveValue("Leaving early");
	// deleting the only range commits instantly: the popover closes and the
	// bar disappears without waiting for a popover close + save
	await popover.getByRole("button", { name: "Delete" }).click();
	await isNotVisible(events.locators.dayEditorPopover);
	await isNotVisible(events.locators.availabilityBars);
	await events.locators.saveWeekButton.click();
	await page.getByTestId("confirm-button").click();
	await expect(page.getByText("Availability saved")).toBeAttached();

	// an empty submitted week is "unavailable all week", not missing
	await events.goto();
	await isNotVisible(events.locators.availabilityBars);
	await isNotVisible(events.weekNotFilledMarker("current"));
})

crosses over to the team schedule and back, keeping the week

Renders/events/t/:customUrl/t/:customUrl/schedule

Setup
Arrange
  1. Create team name “Team Olive” member user ids Sendou → { customUrl }
    const { customUrl } = await factories.TeamFactory.create({
    	name: "Team Olive",
    	memberUserIds: [ADMIN_ID],
    });
SSendou
Events page
Act
  1. Open the events page
    await events.goto();
  2. the week the editor is showing carries over to the team schedule

    Click next week toggle
    await events.locators.nextWeekToggle.click();
  3. Click team schedule link
    await events.locators.teamScheduleLink.click();
Assert
  1. the page URL is new RegExp(`/t/${customUrl}/schedule\\?week=next`)
    await expect(page).toHaveURL(
    	new RegExp(`/t/${customUrl}/schedule\\?week=next`),
    );
Team schedule page
Assert
  1. heatmap is visible
    await expect(schedule.locators.heatmap).toBeVisible();
Act
  1. the week the schedule is showing carries over to the editor

    Click edit availability link
    await schedule.locators.editAvailabilityLink.click();
Assert
  1. the page URL is /\/events\?week=next/
    await expect(page).toHaveURL(/\/events\?week=next/);
Events page
Assert
  1. week not filled marker “next” is visible
    await expect(events.weekNotFilledMarker("next")).toBeVisible();
Source
test("crosses over to the team schedule and back, keeping the week", async ({
	page,
	factories,
}) => {
	const { customUrl } = await factories.TeamFactory.create({
		name: "Team Olive",
		memberUserIds: [ADMIN_ID],
	});

	await impersonate(page, ADMIN_ID);
	await setTimezoneCookie(page);

	const events = new EventsPage(page);
	await events.goto();

	// the week the editor is showing carries over to the team schedule
	await events.locators.nextWeekToggle.click();
	await events.locators.teamScheduleLink.click();
	await expect(page).toHaveURL(
		new RegExp(`/t/${customUrl}/schedule\\?week=next`),
	);

	const schedule = new TeamSchedulePage(page);
	await expect(schedule.locators.heatmap).toBeVisible();

	// the week the schedule is showing carries over to the editor
	await schedule.locators.editAvailabilityLink.click();
	await expect(page).toHaveURL(/\/events\?week=next/);
	await expect(events.weekNotFilledMarker("next")).toBeVisible();
})

shows a commitment as a locked block on the editor

Renders/events

Setup
Arrange
  1. currentWeek = Availability.weekRange(new Date(), MACHINE_TIMEZONE)
    const currentWeek = Availability.weekRange(new Date(), MACHINE_TIMEZONE);
  2. wednesday = Availability.dateInTimezone( currentWeek.startsAt + WEDNESDAY * DAY_SECONDS +…
    const wednesday = Availability.dateInTimezone(
    	currentWeek.startsAt + WEDNESDAY * DAY_SECONDS + DAY_SECONDS / 2,
    	MACHINE_TIMEZONE,
    );
  3. Create team member user ids Sendou → team
    const team = await factories.TeamFactory.create({
    	memberUserIds: [ADMIN_ID],
    });
  4. Create team event team id team.id author Sendou name “VoD review” starts at Availability.localToTimestamp({ date: wednesday, time: "20:00", timezone: MAC… ends at Availability.localToTimestamp({ date: wednesday, time: "21:30", timezone: MAC…
    await factories.TeamEventFactory.create({
    	teamId: team.id,
    	authorId: ADMIN_ID,
    	name: "VoD review",
    	startsAt: Availability.localToTimestamp({
    		date: wednesday,
    		time: "20:00",
    		timezone: MACHINE_TIMEZONE,
    	}),
    	endsAt: Availability.localToTimestamp({
    		date: wednesday,
    		time: "21:30",
    		timezone: MACHINE_TIMEZONE,
    	}),
    });
SSendou
Events page
Act
  1. Open the events page
    await events.goto();
Assert
  1. first commitments is visible
    await expect(events.locators.commitments.first()).toBeVisible();
  2. first commitments reads “VoD review”
    await expect(events.locators.commitments.first()).toHaveText("VoD review");
Source
test("shows a commitment as a locked block on the editor", async ({
	page,
	factories,
}) => {
	const currentWeek = Availability.weekRange(new Date(), MACHINE_TIMEZONE);
	const wednesday = Availability.dateInTimezone(
		currentWeek.startsAt + WEDNESDAY * DAY_SECONDS + DAY_SECONDS / 2,
		MACHINE_TIMEZONE,
	);
	const team = await factories.TeamFactory.create({
		memberUserIds: [ADMIN_ID],
	});
	await factories.TeamEventFactory.create({
		teamId: team.id,
		authorId: ADMIN_ID,
		name: "VoD review",
		startsAt: Availability.localToTimestamp({
			date: wednesday,
			time: "20:00",
			timezone: MACHINE_TIMEZONE,
		}),
		endsAt: Availability.localToTimestamp({
			date: wednesday,
			time: "21:30",
			timezone: MACHINE_TIMEZONE,
		}),
	});

	await impersonate(page, ADMIN_ID);
	await setTimezoneCookie(page);

	const events = new EventsPage(page);
	await events.goto();

	await expect(events.locators.commitments.first()).toBeVisible();
	await expect(events.locators.commitments.first()).toHaveText("VoD review");
})

paints a range reaching past the hours the tracks show

Renders/events

SSendou
Events page
Act
  1. Open the events page
    await events.goto();
  2. the tracks end at 2 AM until they are expanded; the paint runs past their right edge and the window widens to fit what it produced

    Paint availability 2, 0.5, 1.25
    await events.paintAvailability(WEDNESDAY, 0.5, 1.25);
Assert
  1. availability bars has “title” = “8:00 PM – 5:00 AM”
    await expect(events.locators.availabilityBars).toHaveAttribute(
    	"title",
    	"8:00 PM – 5:00 AM",
    );
Source
test("paints a range reaching past the hours the tracks show", async ({
	page,
}) => {
	await impersonate(page, ADMIN_ID);
	await setTimezoneCookie(page);

	const events = new EventsPage(page);
	await events.goto();

	// the tracks end at 2 AM until they are expanded; the paint runs past
	// their right edge and the window widens to fit what it produced
	await events.paintAvailability(WEDNESDAY, 0.5, 1.25);

	await expect(events.locators.availabilityBars).toHaveAttribute(
		"title",
		"8:00 PM – 5:00 AM",
	);
})

opens the day editor on the click following a drag

Renders/events

SSendou
Events page
Act
  1. Open the events page
    await events.goto();
  2. Paint availability 2, 0.3, 0.5
    await events.paintAvailability(WEDNESDAY, 0.3, 0.5);
  3. the drag ends with a click of its own, which must not open the popover without swallowing the click that comes after it either

    Drag availability bar events.locators.availabilityBars, 60
    await events.dragAvailabilityBar(events.locators.availabilityBars, 60);
Assert
  1. day editor popover is not visible
    await isNotVisible(events.locators.dayEditorPopover);
Act
  1. Click availability bars
    await events.locators.availabilityBars.click();
Assert
  1. day editor popover is visible
    await expect(events.locators.dayEditorPopover).toBeVisible();
Source
test("opens the day editor on the click following a drag", async ({
	page,
}) => {
	await impersonate(page, ADMIN_ID);
	await setTimezoneCookie(page);

	const events = new EventsPage(page);
	await events.goto();

	await events.paintAvailability(WEDNESDAY, 0.3, 0.5);
	// the drag ends with a click of its own, which must not open the popover
	// without swallowing the click that comes after it either
	await events.dragAvailabilityBar(events.locators.availabilityBars, 60);
	await isNotVisible(events.locators.dayEditorPopover);

	await events.locators.availabilityBars.click();
	await expect(events.locators.dayEditorPopover).toBeVisible();
})

copies last week's ranges into the current week

Renders/events

Setup
Arrange
  1. lastWeekRange = Availability.weekRange( subWeeks(new Date(), 1), MACHINE_TIMEZONE, )
    const lastWeekRange = Availability.weekRange(
    	subWeeks(new Date(), 1),
    	MACHINE_TIMEZONE,
    );
  2. lastWednesday = Availability.dateInTimezone( lastWeekRange.startsAt + WEDNESDAY * DAY_SECONDS…
    const lastWednesday = Availability.dateInTimezone(
    	lastWeekRange.startsAt + WEDNESDAY * DAY_SECONDS + DAY_SECONDS / 2,
    	MACHINE_TIMEZONE,
    );
  3. Create availability week user Sendou week starts at lastWeekRange.startsAt timezone MACHINE_TIMEZONE slots starts at Availability.localToTimestamp({ date: lastWednesday, time: "19:00", timezone:… ends at Availability.localToTimestamp({ date: lastWednesday, time: "21:00", timezone:…
    await factories.AvailabilityWeekFactory.create({
    	userId: ADMIN_ID,
    	weekStartsAt: lastWeekRange.startsAt,
    	timezone: MACHINE_TIMEZONE,
    	slots: [
    		{
    			startsAt: Availability.localToTimestamp({
    				date: lastWednesday,
    				time: "19:00",
    				timezone: MACHINE_TIMEZONE,
    			}),
    			endsAt: Availability.localToTimestamp({
    				date: lastWednesday,
    				time: "21:00",
    				timezone: MACHINE_TIMEZONE,
    			}),
    		},
    	],
    });
SSendou
Events page
Act
  1. Open the events page
    await events.goto();
Assert
  1. availability bars is not visible
    await isNotVisible(events.locators.availabilityBars);
Act
  1. Click copy last week button
    await events.locators.copyLastWeekButton.click();
Assert
  1. availability bars count is 1
    await expect(events.locators.availabilityBars).toHaveCount(1);
Act
  1. Click save week button
    await events.locators.saveWeekButton.click();
Assert
  1. text “Availability saved” is in the page
    await expect(page.getByText("Availability saved")).toBeAttached();
Source
test("copies last week's ranges into the current week", async ({
	page,
	factories,
}) => {
	const lastWeekRange = Availability.weekRange(
		subWeeks(new Date(), 1),
		MACHINE_TIMEZONE,
	);
	const lastWednesday = Availability.dateInTimezone(
		lastWeekRange.startsAt + WEDNESDAY * DAY_SECONDS + DAY_SECONDS / 2,
		MACHINE_TIMEZONE,
	);
	await factories.AvailabilityWeekFactory.create({
		userId: ADMIN_ID,
		weekStartsAt: lastWeekRange.startsAt,
		timezone: MACHINE_TIMEZONE,
		slots: [
			{
				startsAt: Availability.localToTimestamp({
					date: lastWednesday,
					time: "19:00",
					timezone: MACHINE_TIMEZONE,
				}),
				endsAt: Availability.localToTimestamp({
					date: lastWednesday,
					time: "21:00",
					timezone: MACHINE_TIMEZONE,
				}),
			},
		],
	});

	await impersonate(page, ADMIN_ID);
	await setTimezoneCookie(page);

	const events = new EventsPage(page);
	await events.goto();

	await isNotVisible(events.locators.availabilityBars);
	await events.locators.copyLastWeekButton.click();
	await expect(events.locators.availabilityBars).toHaveCount(1);

	await events.locators.saveWeekButton.click();
	await expect(page.getByText("Availability saved")).toBeAttached();
})

Schedule visibility

stops friends seeing the schedule, then opens it up to a team joined later

N-ZAPSendouteammateevents.spec.ts:284

Renders/events/friends/t/:customUrl/t/:customUrl/schedule

Setup
Arrange
  1. Create named users “Teammate” → [teammate]
    const [teammate] = await createNamedUsers(factories, ["Teammate"]);
  2. N-ZAP is a friend and deliberately not a teammate, so only the friends toggle reaches them

    Create friendship user one Sendou user two N-ZAP
    await factories.FriendshipFactory.create({
    	userOneId: ADMIN_ID,
    	userTwoId: NZAP_TEST_ID,
    });
  3. Create availability week user Sendou week starts at weekRange().startsAt timezone MACHINE_TIMEZONE slots daySlot(WEDNESDAY, "18:00", "22:00")
    await factories.AvailabilityWeekFactory.create({
    	userId: ADMIN_ID,
    	weekStartsAt: weekRange().startsAt,
    	timezone: MACHINE_TIMEZONE,
    	slots: [daySlot(WEDNESDAY, "18:00", "22:00")],
    });
NN-ZAP
Friends page
Act
  1. Open the friends page
    await friends.goto();
Assert
  1. schedule button Sendou is visible
    await expect(friends.scheduleButton(ADMIN_ID)).toBeVisible();
SSendou
Events page
Act
  1. Open the events page
    await events.goto();
Assert
  1. nothing restricted yet, so the editor says nothing about who is left out

    not shared with is not visible
    await isNotVisible(events.locators.notSharedWith);
Act
  1. Set schedule visibility uncheck “All friends”
    await events.setScheduleVisibility({ uncheck: [ALL_FRIENDS] });
Assert
  1. visibility button reads “Limited”
    await expect(events.locators.visibilityButton).toHaveText("Limited");
  2. not shared with reads “· Not shared with friends”
    await expect(events.locators.notSharedWith).toHaveText(
    	"· Not shared with friends",
    );
NN-ZAP
Friends page
Act
  1. Open the friends page
    await friends.goto();
Assert
  1. schedule button Sendou is not visible
    await isNotVisible(friends.scheduleButton(ADMIN_ID));
Arrange
  1. joined after the visibility was saved, so it starts outside the allow-list

    Create team name “Team Olive” member user ids teammate.id, Sendou → team
    const team = await factories.TeamFactory.create({
    	name: "Team Olive",
    	memberUserIds: [teammate.id, ADMIN_ID],
    });
Tteammate
Team schedule page
Act
  1. Open the team schedule page team.customUrl
    await schedule.goto(team.customUrl);
  2. Click grid view tab
    await schedule.locators.gridViewTab.click();
Assert
  1. cell range Sendou, 2 is not visible
    await isNotVisible(schedule.cellRange(ADMIN_ID, WEDNESDAY));
SSendou
Events page
Act
  1. Open the events page
    await events.goto();
Assert
  1. the team joined after the save is outside the allow-list, and named as such

    not shared with reads “· Not shared with friends and Team Olive”
    await expect(events.locators.notSharedWith).toHaveText(
    	"· Not shared with friends and Team Olive",
    );
Act
  1. Set schedule visibility check “Team Olive”
    await events.setScheduleVisibility({ check: ["Team Olive"] });
Assert
  1. not shared with reads “· Not shared with friends”
    await expect(events.locators.notSharedWith).toHaveText(
    	"· Not shared with friends",
    );
Tteammate
Team schedule page
Act
  1. Open the team schedule page team.customUrl
    await schedule.goto(team.customUrl);
  2. Click grid view tab
    await schedule.locators.gridViewTab.click();
Assert
  1. cell range Sendou, 2 is visible
    await expect(schedule.cellRange(ADMIN_ID, WEDNESDAY)).toBeVisible();
NN-ZAP
Friends page
Act
  1. Open the friends page
    await friends.goto();
Assert
  1. schedule button Sendou is not visible
    await isNotVisible(friends.scheduleButton(ADMIN_ID));
Source
test("stops friends seeing the schedule, then opens it up to a team joined later", async ({
	page,
	factories,
}) => {
	const [teammate] = await createNamedUsers(factories, ["Teammate"]);
	// N-ZAP is a friend and deliberately not a teammate, so only the friends toggle reaches them
	await factories.FriendshipFactory.create({
		userOneId: ADMIN_ID,
		userTwoId: NZAP_TEST_ID,
	});
	await factories.AvailabilityWeekFactory.create({
		userId: ADMIN_ID,
		weekStartsAt: weekRange().startsAt,
		timezone: MACHINE_TIMEZONE,
		slots: [daySlot(WEDNESDAY, "18:00", "22:00")],
	});

	const friends = new FriendsPage(page);
	await impersonate(page, NZAP_TEST_ID);
	await setTimezoneCookie(page);
	await friends.goto();

	await expect(friends.scheduleButton(ADMIN_ID)).toBeVisible();

	const events = new EventsPage(page);
	await impersonate(page, ADMIN_ID);
	await events.goto();

	// nothing restricted yet, so the editor says nothing about who is left out
	await isNotVisible(events.locators.notSharedWith);

	await events.setScheduleVisibility({ uncheck: [ALL_FRIENDS] });

	await expect(events.locators.visibilityButton).toHaveText("Limited");
	await expect(events.locators.notSharedWith).toHaveText(
		"· Not shared with friends",
	);

	await impersonate(page, NZAP_TEST_ID);
	await friends.goto();

	await isNotVisible(friends.scheduleButton(ADMIN_ID));

	// joined after the visibility was saved, so it starts outside the allow-list
	const team = await factories.TeamFactory.create({
		name: "Team Olive",
		memberUserIds: [teammate.id, ADMIN_ID],
	});

	const schedule = new TeamSchedulePage(page);
	await impersonate(page, teammate.id);
	await setTimezoneCookie(page);
	await schedule.goto(team.customUrl);
	await schedule.locators.gridViewTab.click();

	await isNotVisible(schedule.cellRange(ADMIN_ID, WEDNESDAY));

	await impersonate(page, ADMIN_ID);
	await events.goto();

	// the team joined after the save is outside the allow-list, and named as such
	await expect(events.locators.notSharedWith).toHaveText(
		"· Not shared with friends and Team Olive",
	);

	await events.setScheduleVisibility({ check: ["Team Olive"] });

	await expect(events.locators.notSharedWith).toHaveText(
		"· Not shared with friends",
	);

	await impersonate(page, teammate.id);
	await schedule.goto(team.customUrl);
	await schedule.locators.gridViewTab.click();

	await expect(schedule.cellRange(ADMIN_ID, WEDNESDAY)).toBeVisible();

	// adding the team did not quietly restore the friends sharing the dialog opened with
	await impersonate(page, NZAP_TEST_ID);
	await friends.goto();

	await isNotVisible(friends.scheduleButton(ADMIN_ID));
})