Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import eslintPluginTsdoc from 'eslint-plugin-tsdoc'
import tseslint from 'typescript-eslint'

export default defineConfig(
globalIgnores(['.output/**', '.wxt/**', 'coverage/**', 'node_modules/**', 'public/**']),
globalIgnores(['.wxt/**', 'coverage/**', 'dist/**', 'node_modules/**', 'public/**']),
eslint.configs.recommended,
tseslint.configs.strictTypeChecked,
tseslint.configs.stylisticTypeChecked,
Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,27 @@
"devDependencies": {
"@eslint/js": "^9.39.3",
"@vitest/eslint-plugin": "^1.6.9",
"@wxt-dev/i18n": "^0.2.4",
"autoprefixer": "^10.4.24",
"@wxt-dev/i18n": "^0.2.5",
"autoprefixer": "^10.4.27",
"esbuild": "^0.27.3",
"eslint": "^9.39.3",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-tsdoc": "^0.5.0",
"eslint-plugin-tsdoc": "^0.5.2",
"husky": "^9.1.7",
"lint-staged": "^16.2.7",
"lint-staged": "^16.3.0",
"postcss": "^8.5.6",
"postcss-for": "^2.1.1",
"postcss-load-config": "^6.0.1",
"prettier": "^3.8.1",
"stylelint": "^17.3.0",
"stylelint": "^17.4.0",
"stylelint-config-standard": "^40.0.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.56.0",
"typescript-eslint": "^8.56.1",
"vite": "^7.3.1",
"vitest": "^4.0.18",
"wxt": "^0.20.17"
"wxt": "^0.20.18"
},
"engines": {
"node": ">=24.0.0"
Expand Down
822 changes: 342 additions & 480 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/entrypoints/content/common/test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { vi } from 'vitest'

import { isPage, Page } from '@/entrypoints/content/common/utils/pages'

/**
* Helper for creating a DOM element with the HTML content for testing.
*
Expand All @@ -10,3 +14,7 @@ export const createElement = (html: string): HTMLDivElement => {

return div
}

export const mockIsPage = (page: Page) => {
vi.mocked(isPage).mockImplementation((...pages: Page[]) => pages.includes(page))
}
3 changes: 0 additions & 3 deletions src/entrypoints/content/common/types/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ export type Module = {
name: string
// Pages where the module should run (see @common/utils/pages.ts for a list of pages)
pages: Page[]
// Pages where the module should not run
// This is only evaluated when `pages: [pages.all]`, otherwise it is ignored
excludePages?: Page[]
// Function containing the module's logic
run: () => void
}
7 changes: 7 additions & 0 deletions src/entrypoints/content/common/utils/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@
* @returns The pathname (e.g., '/Club/Players/')
*/
export const getCurrentPathname = (): string => window.location.pathname

/**
* Get the current page's search params from the browser location.
*
* @returns The URLSearchParams (e.g., '?YouthTeamId=123')
*/
export const getCurrentSearchParams = (): URLSearchParams => new URLSearchParams(window.location.search)
215 changes: 157 additions & 58 deletions src/entrypoints/content/common/utils/pages.test.ts
Original file line number Diff line number Diff line change
@@ -1,81 +1,180 @@
import { describe, expect, it, vi } from 'vitest'

import { getCurrentPathname } from '@/entrypoints/content/common/utils/location'
import { getCurrentPathname, getCurrentSearchParams } from '@/entrypoints/content/common/utils/location'
import { getCurrentPage, isPage, Page, pages } from '@/entrypoints/content/common/utils/pages'
import { isOwnTeamPage } from '@/entrypoints/content/common/utils/team/utils'

vi.mock(import('@/entrypoints/content/common/utils/location'))
vi.mock(import('@/entrypoints/content/common/utils/team/utils'))

describe(isPage, () => {
it.each([
{
desc: 'different path',
currentPathname: '/Foo',
page: new Page('/Bar'),
ownTeamPage: false,
expected: false,
},
{
desc: 'no teamContext, own team page',
currentPathname: '/Foo',
page: new Page('/Foo'),
ownTeamPage: true,
expected: true,
},
{
desc: 'no teamContext, not own team page',
currentPathname: '/Foo',
page: new Page('/Foo'),
ownTeamPage: false,
expected: true,
},
{
desc: 'OWN_TEAM, own team page',
currentPathname: '/Foo',
page: new Page('/Foo', 'OWN_TEAM'),
ownTeamPage: true,
expected: true,
},
{
desc: 'OWN_TEAM, other team page',
currentPathname: '/Foo',
page: new Page('/Foo', 'OWN_TEAM'),
ownTeamPage: false,
expected: false,
},
{
desc: 'OTHER_TEAM, own team page',
currentPathname: '/Foo',
page: new Page('/Foo', 'OTHER_TEAM'),
ownTeamPage: true,
expected: false,
},
{
desc: 'OTHER_TEAM, other team page',
currentPathname: '/Foo',
page: new Page('/Foo', 'OTHER_TEAM'),
ownTeamPage: false,
expected: true,
},
])('$desc', ({ currentPathname, page, ownTeamPage, expected }) => {
vi.mocked(getCurrentPathname).mockReturnValue(currentPathname)
vi.mocked(isOwnTeamPage).mockReturnValue(ownTeamPage)

expect(isPage(page)).toBe(expected)
describe('pathname matching', () => {
it.each([
{
desc: 'returns false when pathnames differ',
currentPathname: '/Foo',
page: new Page('/Bar'),
expected: false,
},
{
desc: 'returns true when pathnames match and there are no options',
currentPathname: '/Foo',
page: new Page('/Foo'),
expected: true,
},
])('$desc', ({ currentPathname, page, expected }) => {
vi.mocked(getCurrentPathname).mockReturnValue(currentPathname)
vi.mocked(getCurrentSearchParams).mockReturnValue(new URLSearchParams())

expect(isPage(page)).toBe(expected)
})
})

describe('teamContext', () => {
it.each([
{
desc: 'no teamContext, matches own team page',
page: new Page('/Foo'),
ownTeamPage: true,
expected: true,
},
{
desc: 'no teamContext, matches other team page',
page: new Page('/Foo'),
ownTeamPage: false,
expected: true,
},
{
desc: 'OWN_TEAM, matches own team page',
page: new Page('/Foo', { teamContext: 'OWN_TEAM' }),
ownTeamPage: true,
expected: true,
},
{
desc: 'OWN_TEAM, does not match own team page',
page: new Page('/Foo', { teamContext: 'OWN_TEAM' }),
ownTeamPage: false,
expected: false,
},
{
desc: 'OTHER_TEAM, does not match own team page',
page: new Page('/Foo', { teamContext: 'OTHER_TEAM' }),
ownTeamPage: true,
expected: false,
},
{
desc: 'OTHER_TEAM, matches other team page',
page: new Page('/Foo', { teamContext: 'OTHER_TEAM' }),
ownTeamPage: false,
expected: true,
},
])('$desc', ({ page, ownTeamPage, expected }) => {
vi.mocked(getCurrentPathname).mockReturnValue('/Foo')
vi.mocked(getCurrentSearchParams).mockReturnValue(new URLSearchParams())
vi.mocked(isOwnTeamPage).mockReturnValue(ownTeamPage)

expect(isPage(page)).toBe(expected)
})
})

describe('queryParams', () => {
it.each([
{
desc: 'matches when required param is present (no value constraint)',
page: new Page('/Foo', { queryParams: [{ name: 'BarId' }] }),
search: '?BarId=123',
expected: true,
},
{
desc: 'does not match when required param is absent',
page: new Page('/Foo', { queryParams: [{ name: 'BarId' }] }),
search: '',
expected: false,
},
{
desc: 'matches when param has the required value',
page: new Page('/Foo', { queryParams: [{ name: 'BarId', value: '123' }] }),
search: '?BarId=123',
expected: true,
},
{
desc: 'does not match when param has a different value',
page: new Page('/Foo', { queryParams: [{ name: 'BarId', value: '123' }] }),
search: '?BarId=456',
expected: false,
},
{
desc: 'does not match when param with required value is absent',
page: new Page('/Foo', { queryParams: [{ name: 'BarId', value: '123' }] }),
search: '',
expected: false,
},
{
desc: 'matches when all required params are present',
page: new Page('/Foo', { queryParams: [{ name: 'foo' }, { name: 'bar' }] }),
search: '?foo=1&bar=2',
expected: true,
},
{
desc: 'does not match when only some required params are present',
page: new Page('/Foo', { queryParams: [{ name: 'foo' }, { name: 'bar' }] }),
search: '?foo=1',
expected: false,
},
])('$desc', ({ page, search, expected }) => {
vi.mocked(getCurrentPathname).mockReturnValue('/Foo')
vi.mocked(getCurrentSearchParams).mockReturnValue(new URLSearchParams(search))
vi.mocked(isOwnTeamPage).mockReturnValue(false)

expect(isPage(page)).toBe(expected)
})
})
})

describe(getCurrentPage, () => {
it('returns the current page', () => {
it('returns the correct page for a simple pathname', () => {
vi.mocked(getCurrentPathname).mockReturnValue(pages.club.pathname)
vi.mocked(getCurrentSearchParams).mockReturnValue(new URLSearchParams())
vi.mocked(isOwnTeamPage).mockReturnValue(false)

expect(getCurrentPage()).toBe(pages.club)
})

it('throws an error when no page matches', () => {
it('distinguishes own team player list from other team player list', () => {
vi.mocked(getCurrentPathname).mockReturnValue(pages.playerList.senior.own.pathname)
vi.mocked(getCurrentSearchParams).mockReturnValue(new URLSearchParams())
vi.mocked(isOwnTeamPage).mockReturnValue(true)

expect(getCurrentPage()).toBe(pages.playerList.senior.own)
})

it('distinguishes other team player list from own team player list', () => {
vi.mocked(getCurrentPathname).mockReturnValue(pages.playerList.senior.other.pathname)
vi.mocked(getCurrentSearchParams).mockReturnValue(new URLSearchParams())
vi.mocked(isOwnTeamPage).mockReturnValue(false)

expect(getCurrentPage()).toBe(pages.playerList.senior.other)
})

it('distinguishes youth matches from senior matches by query param', () => {
vi.mocked(getCurrentPathname).mockReturnValue(pages.matchList.youth.own.pathname)
vi.mocked(getCurrentSearchParams).mockReturnValue(new URLSearchParams('?YouthTeamId=123'))
vi.mocked(isOwnTeamPage).mockReturnValue(true)

expect(getCurrentPage()).toBe(pages.matchList.youth.own)
})

it('returns senior matches when YouthTeamId param is absent', () => {
vi.mocked(getCurrentPathname).mockReturnValue(pages.matchList.senior.own.pathname)
vi.mocked(getCurrentSearchParams).mockReturnValue(new URLSearchParams())
vi.mocked(isOwnTeamPage).mockReturnValue(true)

expect(getCurrentPage()).toBe(pages.matchList.senior.own)
})

it('throws error when no page matches', () => {
vi.mocked(getCurrentPathname).mockReturnValue('/Unsupported/Path/')
vi.mocked(getCurrentSearchParams).mockReturnValue(new URLSearchParams())
vi.mocked(isOwnTeamPage).mockReturnValue(false)

expect(() => getCurrentPage()).toThrowError('The current page is not supported')
Expand Down
Loading