Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4b199d4
Saving progress on release note search
Thomas-Gallant May 3, 2026
0fc99cb
Adding exit button to the search menu
Thomas-Gallant May 3, 2026
fd03f3b
Adding date picker filter
Thomas-Gallant May 3, 2026
e2be6dc
Merge branch 'main' into release-notes-search
Thomas-Gallant May 8, 2026
2e77702
Merge branch 'main' into release-notes-search
Thomas-Gallant Aug 23, 2026
4b12857
Improving theme coloring
Thomas-Gallant Aug 23, 2026
ea57a04
Adding playwright tests
Thomas-Gallant Aug 23, 2026
3933153
Fixing date bug
Thomas-Gallant Aug 23, 2026
f62c446
All filter buttons fit on mobile
Thomas-Gallant Aug 23, 2026
29e9e2f
Fixing assorted bugs
Thomas-Gallant Aug 23, 2026
252fbba
Reducing duplication by creating subcomponents
Thomas-Gallant Aug 23, 2026
8a5f15c
Updating changelog
Thomas-Gallant Aug 23, 2026
3fb8a00
Resolving sonarqube fidnings
Thomas-Gallant Aug 23, 2026
6b14c8f
Resolving review finding
Thomas-Gallant Aug 23, 2026
2f2aa3c
updating with main branch
Thomas-Gallant Sep 5, 2026
759a747
Fail fast validation
Thomas-Gallant Sep 5, 2026
048c94f
Adding more test cases and adding bun to allowScripts
Thomas-Gallant Sep 5, 2026
33f391d
resolving feedback from comments
Thomas-Gallant Sep 5, 2026
2459a73
resolving sonar issue
Thomas-Gallant Sep 5, 2026
eac2641
fixing date filtering issue
Thomas-Gallant Sep 5, 2026
70a401d
Updating the filtering logic so that changing the year updates immedi…
Thomas-Gallant Sep 5, 2026
f680e7a
Hardening bun install on CI
Thomas-Gallant Sep 5, 2026
755e44e
simplifying implementation
Thomas-Gallant Sep 5, 2026
d47373c
Fixing ci bun test
Thomas-Gallant Sep 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ runs:
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: "1.3.14"
bun-version: "1.4.2"
- name: Install script dependencies
shell: bash
working-directory: .github/scripts
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,19 @@ jobs:
uses: actions/setup-node@v6
with:
node-version: 26.5.0
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: "1.4.2"
- name: 📦 Install dependencies
run: npm ci --ignore-scripts
- name: Lint Check
Comment thread
Thomas-Gallant marked this conversation as resolved.
run: npm run lint
- name: Setup Changelog
uses: ./.github/workflows/composite-actions/set-changelog-date
- name: Release Notes Tests
working-directory: sudokuru
run: bun test
- name: Typescript Check
working-directory: sudokuru
run: npm run tsc
Expand Down
200 changes: 197 additions & 3 deletions e2e/web/page/releasenotes.page.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,187 @@
import { Locator, Page, expect } from "@playwright/test";
import { ReleaseNoteInterface } from "../../../sudokuru/app/Components/ReleaseNotes/ReleaseNote";
import { parseChangelogDate } from "../../../sudokuru/app/Components/ReleaseNotes/ReleaseNoteFunctions";
import { HeaderComponent } from "../components/header.component";

export const INITIAL_RELEASE_NOTES_COUNT = 9;

export class ReleaseNotesPage {
readonly page: Page;
readonly title: Locator;
readonly search: Locator;
readonly searchClear: Locator;
readonly resultCount: Locator;
readonly clearFilters: Locator;
readonly targetsFilter: Locator;
readonly targetsMenu: Locator;
readonly targetsMenuClose: Locator;
readonly categoriesFilter: Locator;
readonly categoriesMenu: Locator;
readonly categoriesMenuClose: Locator;
readonly contributorsFilter: Locator;
readonly contributorsMenu: Locator;
readonly contributorsMenuClose: Locator;
readonly startDateFilter: Locator;
readonly startDateMenu: Locator;
readonly startDateMenuClose: Locator;
readonly startYear: Locator;
readonly startMonth: Locator;
readonly endDateFilter: Locator;
readonly endDateMenu: Locator;
readonly endDateMenuClose: Locator;
readonly endYear: Locator;
readonly endMonth: Locator;

constructor(page: Page) {
this.page = page;
this.title = page.getByTestId("ReleaseNotesTitle");
this.search = page.getByPlaceholder("Search release notes…");
this.searchClear = page.getByTestId("ReleaseNotesSearchClearButton");
this.resultCount = page.getByTestId("ReleaseNotesResultCount");
this.clearFilters = page.getByTestId("ReleaseNotesClearFiltersButton");
this.targetsFilter = page.getByTestId("ReleaseNotesTargetsFilterButton");
this.targetsMenu = page.getByTestId("ReleaseNotesTargetsFilterMenu");
this.targetsMenuClose = page.getByTestId(
"ReleaseNotesTargetsFilterCloseButton",
);
this.categoriesFilter = page.getByTestId(
"ReleaseNotesCategoriesFilterButton",
);
this.categoriesMenu = page.getByTestId("ReleaseNotesCategoriesFilterMenu");
this.categoriesMenuClose = page.getByTestId(
"ReleaseNotesCategoriesFilterCloseButton",
);
this.contributorsFilter = page.getByTestId(
"ReleaseNotesContributorsFilterButton",
);
this.contributorsMenu = page.getByTestId(
"ReleaseNotesContributorsFilterMenu",
);
this.contributorsMenuClose = page.getByTestId(
"ReleaseNotesContributorsFilterCloseButton",
);
this.startDateFilter = page.getByTestId(
"ReleaseNotesStartDateFilterButton",
);
this.startDateMenu = page.getByTestId("ReleaseNotesStartDateFilterMenu");
this.startDateMenuClose = page.getByTestId(
"ReleaseNotesStartDateFilterCloseButton",
);
this.startYear = page.getByTestId("ReleaseNotesStartYearButton");
this.startMonth = page.getByTestId("ReleaseNotesStartMonthButton");
this.endDateFilter = page.getByTestId("ReleaseNotesEndDateFilterButton");
this.endDateMenu = page.getByTestId("ReleaseNotesEndDateFilterMenu");
this.endDateMenuClose = page.getByTestId(
"ReleaseNotesEndDateFilterCloseButton",
);
this.endYear = page.getByTestId("ReleaseNotesEndYearButton");
this.endMonth = page.getByTestId("ReleaseNotesEndMonthButton");
}

targetOption(target: string) {
return this.page.getByTestId(`ReleaseNotesTargetOption-${target}`);
}

categoryOption(category: string) {
return this.page.getByTestId(`ReleaseNotesCategoryOption-${category}`);
}

contributorOption(contributor: string) {
return this.page.getByTestId(
`ReleaseNotesContributorOption-${contributor}`,
);
}

startYearOption(year: number) {
return this.page.getByTestId(`ReleaseNotesStartYearOption-${year}`);
}

startMonthOption(month: string) {
return this.page.getByTestId(`ReleaseNotesStartMonthOption-${month}`);
}

endYearOption(year: number) {
return this.page.getByTestId(`ReleaseNotesEndYearOption-${year}`);
}

endMonthOption(month: string) {
return this.page.getByTestId(`ReleaseNotesEndMonthOption-${month}`);
}

async navigateToPage() {
const headerComponent = new HeaderComponent(this.page);
await headerComponent.drawer.click();
await headerComponent.releaseNotes.click();
await this.releaseNotesPageIsRendered();
}

async searchFor(keyword: string) {
Comment thread
Gregory711 marked this conversation as resolved.
await this.search.fill(keyword);
await expect(this.searchClear).toBeInViewport({ ratio: 1 });
}

async clearSearch() {
await this.searchClear.click();
await expect(this.search).toHaveValue("");
await expect(this.searchClear).toHaveCount(0);
}

parseChangelogDate(date: string) {
return parseChangelogDate(date);
}

async openMenu(button: Locator, menu: Locator) {
await button.click();
await this.menuIsOpen(menu);
}

async closeMenu(closeButton: Locator, menu: Locator) {
await closeButton.click();
await this.menuIsClosed(menu);
}

async toggleTarget(target: string) {
const option = this.targetOption(target);
await expect(option).toBeInViewport({ ratio: 1 });
await option.click();
}

async toggleCategory(category: string) {
const option = this.categoryOption(category);
await expect(option).toBeInViewport({ ratio: 1 });
await option.click();
}

async toggleContributor(contributor: string) {
const option = this.contributorOption(contributor);
await expect(option).toBeInViewport({ ratio: 1 });
await option.click();
}

async selectStartDate(year: number, month: string) {
await this.openMenu(this.startDateFilter, this.startDateMenu);
await this.startYear.click();
const yearOption = this.startYearOption(year);
await expect(yearOption).toBeInViewport({ ratio: 1 });
await yearOption.click();
await this.startMonth.click();
const monthOption = this.startMonthOption(month);
await expect(monthOption).toBeInViewport({ ratio: 1 });
await monthOption.click();
await this.menuIsClosed(this.startDateMenu);
}

async selectEndDate(year: number, month: string) {
await this.openMenu(this.endDateFilter, this.endDateMenu);
await this.endYear.click();
const yearOption = this.endYearOption(year);
await expect(yearOption).toBeInViewport({ ratio: 1 });
await yearOption.click();
await this.endMonth.click();
const monthOption = this.endMonthOption(month);
await expect(monthOption).toBeInViewport({ ratio: 1 });
await monthOption.click();
await this.menuIsClosed(this.endDateMenu);
}

async releaseNotesPageIsRendered() {
Expand All @@ -18,6 +192,26 @@ export class ReleaseNotesPage {
await expect(this.page.getByText(version)).toBeInViewport({ ratio: 1 });
}

async releaseNoteIsNotRendered(version: string) {
await expect(this.page.getByTestId(version)).toHaveCount(0);
}

async resultCountIs(count: number) {
await expect(this.resultCount).toHaveText(`${count} releases`);
}

async filterButtonLabelIs(button: Locator, label: string) {
await expect(button).toHaveText(label);
}

async menuIsOpen(menu: Locator) {
await expect(menu).toBeInViewport({ ratio: 1 });
}

async menuIsClosed(menu: Locator) {
await expect(menu).not.toBeInViewport({ ratio: 1 });
}

/**
* We load in 9 elements at a time until we have loaded in the release not we want to interact with
* @param notes All of the release notes in json format
Expand All @@ -28,17 +222,17 @@ export class ReleaseNotesPage {
version: string,
) {
let index = 0;
const LOADED_RELEASE_NOTES = 9;
for (const [key, note] of notes.entries()) {
if (note.version === version) {
index = key;
}
}
const numberOfLoads = Math.ceil(index / LOADED_RELEASE_NOTES);
const numberOfLoads = Math.ceil(index / INITIAL_RELEASE_NOTES_COUNT);

for (let i = 0; i < numberOfLoads; i++) {
const loaderLocator = this.page.getByTestId(
notes[LOADED_RELEASE_NOTES + LOADED_RELEASE_NOTES * i].version,
notes[INITIAL_RELEASE_NOTES_COUNT + INITIAL_RELEASE_NOTES_COUNT * i]
.version,
);
await loaderLocator.scrollIntoViewIfNeeded();
}
Expand Down
Loading
Loading