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
4 changes: 2 additions & 2 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"publy/no-manual-response-message-translation": "error",
"publy/arrow-function-components": "error",
"publy/prefer-query-display": "error",
"anti-slop/no-chained-type-assertions": "off",
"anti-slop/no-chained-type-assertions": "error",
"anti-slop/no-conditional-empty-object-spread": "error",
"anti-slop/no-known-value-widening": "off",
"anti-slop/no-module-mocking": "off",
Expand All @@ -80,7 +80,7 @@
"anti-slop/no-runtime-typeof": "off",
"anti-slop/no-shape-in-symbol-names": "error",
"anti-slop/no-unknown-parameters": "off",
"anti-slop/no-unknown-returns": "off",
"anti-slop/no-unknown-returns": "error",
"anti-slop/no-unknown-type-aliases": "error",
"anti-slop/no-unsafe-dictionary-type": "off",
"anti-slop/no-widen-then-assert": "error",
Expand Down
5 changes: 3 additions & 2 deletions apps/front/e2e/auth-error.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ test.describe(
await expect(page.getByTestId('view-404')).toBeVisible();

await page.evaluate(() => {
(window as unknown as { __spaAlive?: boolean }).__spaAlive = true;
(window as { __spaAlive?: boolean } & typeof window).__spaAlive = true;
});
await page
.getByTestId('view-404')
Expand All @@ -531,7 +531,8 @@ test.describe(

const alive = await page.evaluate(
() =>
(window as unknown as { __spaAlive?: boolean }).__spaAlive === true,
(window as { __spaAlive?: boolean } & typeof window).__spaAlive ===
true,
);
expect(alive).toBe(true);
});
Expand Down
4 changes: 2 additions & 2 deletions apps/front/e2e/parity-happy-path.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const getThemeState = async (page: Page) =>
}));

const extractSeededEmails = async (response: {
json: () => Promise<unknown>;
json: () => Promise<{ data?: unknown }>;
}): Promise<string[]> => {
const payload = (await response.json()) as {
data?: unknown;
Expand All @@ -97,7 +97,7 @@ const mapRowsEmails = (rows: unknown[]): string[] => {
};

const extractProfiles = async (response: {
json: () => Promise<unknown>;
json: () => Promise<{ data?: unknown }>;
}): Promise<StaffProfileFixture[]> => {
const payload = (await response.json()) as {
data?: unknown;
Expand Down
2 changes: 1 addition & 1 deletion apps/front/e2e/staff-profiles.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const waitForStaffProfilesGetResponse = (page: Page, expectedQuery?: string) =>
});

const extractProfiles = async (response: {
json: () => Promise<unknown>;
json: () => Promise<{ data?: unknown }>;
}): Promise<StaffProfileFixture[]> => {
const payload = (await response.json()) as {
data?: unknown;
Expand Down
5 changes: 3 additions & 2 deletions apps/front/e2e/staff-tenant-details.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ test.describe(
await expect(page.getByTestId('staff-tenant-details-page')).toBeVisible();

await page.evaluate(() => {
(window as unknown as { __spaAlive?: boolean }).__spaAlive = true;
(window as { __spaAlive?: boolean } & typeof window).__spaAlive = true;
});

await page.getByRole('link', { name: 'Users' }).click();
Expand All @@ -282,7 +282,8 @@ test.describe(

const alive = await page.evaluate(
() =>
(window as unknown as { __spaAlive?: boolean }).__spaAlive === true,
(window as { __spaAlive?: boolean } & typeof window).__spaAlive ===
true,
);
expect(alive).toBe(true);
});
Expand Down
49 changes: 19 additions & 30 deletions apps/front/e2e/toast-contrast.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,21 +298,15 @@ const readBrowserPaint = async (
earlyAssert(
{
backgroundClip: ps.backgroundClip,
webkitBackgroundClip: (
ps as unknown as Record<string, string | undefined>
)['webkitBackgroundClip'],
webkitTextFillColor: (
ps as unknown as Record<string, string | undefined>
)['webkitTextFillColor'],
webkitBackgroundClip:
ps.getPropertyValue('-webkit-background-clip') || undefined,
webkitTextFillColor:
ps.getPropertyValue('-webkit-text-fill-color') || undefined,
color: ps.color,
opacity: ps.opacity,
maskImage: (ps as unknown as Record<string, string | undefined>)[
'maskImage'
],
mask: (ps as unknown as Record<string, string | undefined>)[
'mask'
],
} as Record<string, string | undefined>,
maskImage: ps.getPropertyValue('mask-image') || undefined,
mask: ps.getPropertyValue('mask') || undefined,
},
label,
);
for (
Expand Down Expand Up @@ -1242,21 +1236,18 @@ const readBrowserPaint = async (
evaluateClassifier(
{
backgroundClip: painterStyle.backgroundClip,
webkitBackgroundClip: (
painterStyle as unknown as Record<string, string | undefined>
)['webkitBackgroundClip'],
webkitTextFillColor: (
painterStyle as unknown as Record<string, string | undefined>
)['webkitTextFillColor'],
webkitBackgroundClip:
painterStyle.getPropertyValue('-webkit-background-clip') ||
undefined,
webkitTextFillColor:
painterStyle.getPropertyValue('-webkit-text-fill-color') ||
undefined,
color: painterStyle.color,
opacity: painterStyle.opacity,
maskImage: (
painterStyle as unknown as Record<string, string | undefined>
)['maskImage'],
mask: (
painterStyle as unknown as Record<string, string | undefined>
)['mask'],
} as Record<string, string | undefined>,
maskImage:
painterStyle.getPropertyValue('mask-image') || undefined,
mask: painterStyle.getPropertyValue('mask') || undefined,
},
painterName,
);
// Walk ancestors for opacity:0 — text inherits invisibility even if painter itself is opaque
Expand All @@ -1279,9 +1270,7 @@ const readBrowserPaint = async (
);
}
toSrgb(
(painterStyle as unknown as Record<string, string | undefined>)[
'webkitTextFillColor'
] || painterStyle.color,
painterStyle.webkitTextFillColor || painterStyle.color,
`${painterName} text fill colour`,
);
}
Expand Down Expand Up @@ -1502,7 +1491,7 @@ const measurePaintedContrast = async (
const canvas = {
width: __publyImageData.width,
height: __publyImageData.height,
} as unknown as HTMLCanvasElement;
} as HTMLCanvasElement;
const data = __publyImageData.data;

const luminance = ([r, g, b]: number[]): number => {
Expand Down
6 changes: 4 additions & 2 deletions apps/front/src/components/app-shell/app-shell.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ vi.mock('@tanstack/react-router', () => ({
}) => {
const resolvedSearch =
typeof search === 'function'
? (search as (prev: unknown) => unknown)(mocks.linkPrevSearch)
? (search as (prev: unknown) => Record<string, unknown>)(
mocks.linkPrevSearch,
)
: search;
return createElement(
'a',
Expand All @@ -55,7 +57,7 @@ vi.mock('@tanstack/react-router', () => ({
children,
);
},
useMatches: ({ select }: { select: (matches: unknown[]) => unknown }) =>
useMatches: ({ select }: { select: (matches: unknown[]) => void }) =>
select(mocks.matches),
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,5 @@ export const renderMarketing = async (
}
});

return { ...result, router: router as unknown as AnyRouter };
return { ...result, router: router as AnyRouter };
};
35 changes: 13 additions & 22 deletions apps/front/src/components/ui/drawer-form.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ import type { AnyNode, AtRule, Rule } from 'postcss';
import { createElement, type ReactNode } from 'react';
import { useForm } from 'react-hook-form';
import {
Node,
Project,
SyntaxKind,
ts,
Expand All @@ -192,6 +193,7 @@ import {
type CaseClause,
type CatchClause,
type ConditionalExpression,
type ElementAccessExpression,
type FunctionDeclaration,
type GetAccessorDeclaration,
type IfStatement,
Expand All @@ -204,7 +206,6 @@ import {
type JsxSelfClosingElement,
type LabeledStatement,
type MethodDeclaration,
type Node,
type ObjectLiteralExpression,
type PrefixUnaryExpression,
type PropertyAccessExpression,
Expand Down Expand Up @@ -6189,11 +6190,7 @@ const isTracedArrayLiteralUnsafe = (
}
if (prop.getKind() === SyntaxKind.SpreadAssignment) {
return argumentReferencesTracedArray(
(
prop as unknown as {
getExpression(): Node;
}
).getExpression(),
prop.asKindOrThrow(SyntaxKind.SpreadAssignment).getExpression(),
);
}
return false;
Expand Down Expand Up @@ -6257,11 +6254,7 @@ const isTracedArrayLiteralUnsafe = (
const left = binaryExpression.getLeft();
if (left.getKind() === SyntaxKind.ElementAccessExpression) {
const elementBase = unwrapExpression(
(
left as unknown as {
getExpression(): Node;
}
).getExpression(),
(left as ElementAccessExpression).getExpression(),
);
if (writesToTracedArray(elementBase)) {
return true;
Expand Down Expand Up @@ -6664,9 +6657,9 @@ const classifyObjectLiteralReference = (
continue;
}
if (prop.getKind() === SyntaxKind.SpreadAssignment) {
const argument = (
prop as unknown as { getExpression(): Node }
).getExpression();
const argument = prop
.asKindOrThrow(SyntaxKind.SpreadAssignment)
.getExpression();
if (!argument) {
anyUnresolved = true;
continue;
Expand Down Expand Up @@ -8209,15 +8202,13 @@ const collectStatementReturns = (
continue;
}
if (
kind === SyntaxKind.ForStatement ||
kind === SyntaxKind.ForInStatement ||
kind === SyntaxKind.ForOfStatement ||
kind === SyntaxKind.WhileStatement ||
kind === SyntaxKind.DoStatement
Node.isForStatement(statement) ||
Node.isForInStatement(statement) ||
Node.isForOfStatement(statement) ||
Node.isWhileStatement(statement) ||
Node.isDoStatement(statement)
) {
const loopBody = (
statement as unknown as { getStatement(): Statement }
).getStatement();
const loopBody = statement.getStatement();
const loopCollected = collectStatementReturns(asStatementList(loopBody));
if (loopCollected === null) {
return null;
Expand Down
14 changes: 2 additions & 12 deletions apps/front/src/i18n/locales/locales.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,8 @@ const marketingCopy = (bundle: {
landing: Record<string, string>;
}): Record<string, string> => ({ ...bundle.common, ...bundle.landing });

const enMarketing = marketingCopy(
en as unknown as {
common: Record<string, string>;
landing: Record<string, string>;
},
);
const frMarketing = marketingCopy(
fr as unknown as {
common: Record<string, string>;
landing: Record<string, string>;
},
);
const enMarketing = marketingCopy(en);
const frMarketing = marketingCopy(fr);

describe('front locale manifests', () => {
test('publish every registered namespace in registry order', () => {
Expand Down
2 changes: 1 addition & 1 deletion apps/front/src/lib/i18n/trans-render.guard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ describe('real-<Trans> render guard (#1269)', () => {
'Trans must be the real react-i18next component, not a vi.fn',
).toBe(false);
expect(
(initReactI18next as unknown as { type?: string }).type,
(initReactI18next as { type?: string }).type,
'initReactI18next must be the real react-i18next plugin object',
).toBe('3rdParty');
});
Expand Down
6 changes: 4 additions & 2 deletions apps/front/src/lib/locale-switch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import {
postBroadcast,
} from '~/lib/tab-sync/broadcast-sync';

type RouterLike = { invalidate: () => Promise<unknown> };
type LocaleSwitchedResult = { locale: SupportedLanguage };

type RouterLike = { invalidate: () => Promise<void> };
type SetLocaleFn = (opts: {
data: { locale: SupportedLanguage };
}) => Promise<unknown>;
}) => Promise<LocaleSwitchedResult>;

/**
* Re-runs the root loader so it re-resolves `{ locale, resources }` from the
Expand Down
30 changes: 23 additions & 7 deletions apps/front/src/lib/navigation/breadcrumb-contract.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ const buildSyntheticParams = (segments: string[]): Record<string, string> => {
*/
type EntityRegistryEntry = {
query: (params: Record<string, string>) => EntityCrumbQuery;
buildPayload: (marker: string) => unknown;
buildPayload: (marker: string) => Record<string, unknown>;
};

const ENTITY_QUERY_REGISTRY: readonly EntityRegistryEntry[] = [
Expand Down Expand Up @@ -245,7 +245,7 @@ const ENTITY_QUERY_REGISTRY: readonly EntityRegistryEntry[] = [
createRouter({ routeTree, history: createMemoryHistory() } as never);

describe('breadcrumb contract — route-tree walk (#973 Tier 2, guard A)', () => {
const allRoutes = walkRealRouteTree(routeTree as unknown as RouteLike);
const allRoutes = walkRealRouteTree(routeTree as RouteLike);

test('the walk is not vacuous: it visits as many routes as the virtual route config declares', () => {
const expectedCount = countVirtualRouteNodes(routes);
Expand Down Expand Up @@ -535,17 +535,31 @@ describe('breadcrumb contract — route-tree walk (#973 Tier 2, guard A)', () =>
*/
type FakeClientCall = { path: readonly string[]; args: readonly unknown[] };

/** Whatever the fake Kiota chain resolves to — tests read it back through
* `respond` mock assertions, never by consuming the value itself. */
type FakeResponse = Record<string, unknown>;

/** One link in the fake Kiota method chain — a callable Proxy whose
* properties continue the chain and whose call resolves an HTTP verb
* segment. Declared as an interface because the index signature points
* back at the same type (a self-referencing type alias is circular). */
interface FakeApiClient {
(): Promise<FakeResponse>;
[segment: string]: FakeApiClient;
}

const mocks = vi.hoisted(() => {
const HTTP_VERBS = new Set(['get', 'post', 'patch', 'delete', 'put']);

/** A generic stand-in for the Kiota-generated `ApiClient`: every property
* access continues the method chain (`.staff.tenants.byTenantId(id)`),
* and calling a chain whose last segment is an HTTP verb resolves via
* `respond`. */

const buildFakeApiClient = (
respond: (call: FakeClientCall) => unknown,
): unknown => {
const makeProxy = (path: readonly string[]): unknown =>
respond: (call: FakeClientCall) => FakeResponse | Promise<FakeResponse>,
): FakeApiClient => {
const makeProxy = (path: readonly string[]): FakeApiClient =>
new Proxy(() => {}, {
get: (_target, prop) =>
typeof prop === 'string' ? makeProxy([...path, prop]) : undefined,
Expand All @@ -559,12 +573,14 @@ const mocks = vi.hoisted(() => {
// at the same logical position so `.get()` can follow.
return makeProxy(path);
},
});
}) as FakeApiClient;

return makeProxy([]);
};

const respond = vi.fn((_call: FakeClientCall): unknown => ({}));
const respond = vi.fn(
(_call: FakeClientCall): FakeResponse | Promise<FakeResponse> => ({}),
);
const fakeClient = buildFakeApiClient((call) => respond(call));

return { respond, fakeClient };
Expand Down
Loading
Loading