chore: fix TS types (#21)

* chore: fix TS types

* build: add types-check CI job

* style: apply TS style fixes
This commit is contained in:
Mazen Touati
2025-11-18 19:29:37 +01:00
committed by GitHub
parent ed5dfa4664
commit 65d2a240b5
12 changed files with 99 additions and 31 deletions

32
.github/workflows/types-check.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: run-types-check
on:
push:
paths:
- '**.ts'
- '**.js'
- '.github/workflows/types-check.yml'
- 'package.json'
- 'package-lock.json'
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
if: ${{ !env.ACT }}
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run type:check

View File

@@ -19,7 +19,7 @@ export const useConfigStore = defineStore('config', () => {
? JSON.parse(window.Nimbus.headers as string)
: [];
const currentUser = window.Nimbus?.currentUser
? JSON.parse(window.Nimbus.currentUser)
? JSON.parse(window.Nimbus.currentUser as string)
: null;
// Derived values

View File

@@ -28,7 +28,7 @@ export const useRoutesStore = defineStore('routes', () => {
error.value = null;
try {
const source = window.Nimbus.routes;
const source = window.Nimbus?.routes ?? '[]';
if (typeof source !== 'string') {
routes.value = null;
@@ -50,7 +50,7 @@ export const useRoutesStore = defineStore('routes', () => {
const initializeRoutes = async () => {
routeExtractorException.value = parseRouteExtractionException(
window.Nimbus.routeExtractorException,
(window.Nimbus?.routeExtractorException as string) ?? null,
);
await fetchAvailableRoutes();

View File

@@ -35,7 +35,7 @@ vi.mock('@/stores', async importOriginal => {
const renderComponent = () => renderWithProviders(RequestHeaders);
const setPendingRequest = (request: PendingRequest | null) => {
mockRequestStore.pendingRequestData = ref(request);
mockRequestStore.pendingRequestData = ref(request) as any; // eslint-disable-line @typescript-eslint/no-explicit-any
};
describe('RequestHeaders', () => {
@@ -162,7 +162,7 @@ describe('RequestHeaders', () => {
});
it('merges existing request headers with global ones when changing endpoints', async () => {
mockRequestStore.pendingRequestData.headers = [
(mockRequestStore.pendingRequestData as PendingRequest).headers = [
{ key: 'X-Existing', value: '123' },
{ key: 'X-Global', value: 'custom' },
];

View File

@@ -7,7 +7,7 @@ import { nextTick, Reactive, reactive } from 'vue';
const mockRequestStore: Reactive<{
pendingRequestData: object | null;
cancelCurrentRequest: MockedFunction<unknown>;
cancelCurrentRequest: MockedFunction<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
}> = reactive({
pendingRequestData: null,
cancelCurrentRequest: vi.fn(),

View File

@@ -1,7 +1,7 @@
import ResponseViewer from '@/components/domain/Client/Response/ResponseViewer.vue';
import { renderWithProviders, screen } from '@/tests/_utils/test-utils';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { reactive } from 'vue';
import { Reactive, reactive } from 'vue';
vi.mock('@/components/domain/Client/Response/ResponseStatus/ResponseStatus.vue', () => ({
default: {
@@ -32,7 +32,10 @@ vi.mock('@/components/domain/Client/Response/ResponseViewerResponse.vue', () =>
},
}));
const mockRequestHistoryStore = reactive({
const mockRequestHistoryStore: Reactive<{
logs: Array<object> | [];
lastLog: object | null;
}> = reactive({
logs: [],
lastLog: null,
});
@@ -59,7 +62,12 @@ describe('ResponseViewer', () => {
});
it('renders error component when last log contains error', () => {
mockRequestHistoryStore.logs = [{ error: { message: 'Something went wrong' } }];
mockRequestHistoryStore.logs = [
{
error: { message: 'Something went wrong' },
},
];
mockRequestHistoryStore.lastLog = mockRequestHistoryStore.logs[0];
renderWithProviders(ResponseViewer);

View File

@@ -35,13 +35,41 @@ const createPendingRequest = (): PendingRequest => ({
body: {},
payloadType: RequestBodyTypeEnum.JSON,
schema: {
shape: { properties: { name: { type: 'string' } } },
shape: {
'x-name': 'root',
'x-required': true,
properties: {
name: {
'x-name': 'name',
'x-required': false,
type: 'string',
},
},
},
extractionErrors: null,
},
queryParameters: [],
authorization: { type: AuthorizationType.None },
supportedRoutes: [],
routeDefinition: null,
routeDefinition: {
method: 'POST',
endpoint: 'api/users',
shortEndpoint: 'api/users',
schema: {
shape: {
'x-name': 'root',
'x-required': true,
properties: {
name: {
'x-name': 'name',
'x-required': false,
type: 'string',
},
},
},
extractionErrors: null,
},
},
isProcessing: false,
wasExecuted: false,
durationInMs: 0,

View File

@@ -1,10 +1,11 @@
import { useConfigStore } from '@/stores/core/useConfigStore';
import { createPinia, setActivePinia } from 'pinia';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { NimbusConfig } from '../../../types/global';
declare global {
interface Window {
Nimbus?: Record<string, unknown>;
Nimbus: NimbusConfig;
}
}
@@ -26,6 +27,8 @@ describe('useConfigStore', () => {
isVersioned: true,
headers: JSON.stringify([{ header: 'X-Test', type: 'raw', value: '123' }]),
currentUser: JSON.stringify({ id: 99 }),
routes: '',
routeExtractorException: null,
};
const store = useConfigStore();
@@ -37,17 +40,4 @@ describe('useConfigStore', () => {
expect(store.isLoggedIn).toBe(true);
expect(store.userId).toBe(99);
});
it('falls back to defaults when Nimbus undefined', () => {
window.Nimbus = undefined;
const store = useConfigStore();
expect(store.apiUrl).toBe('http://localhost');
expect(store.appBasePath).toBe('');
expect(store.headers).toEqual([]);
expect(store.isVersioned).toBe(false);
expect(store.isLoggedIn).toBe(false);
expect(store.userId).toBeNull();
});
});

View File

@@ -1,3 +1,4 @@
import { AuthorizationContract } from '@/interfaces';
import { AuthorizationType } from '@/interfaces/generated';
import { PendingRequest, RequestBodyTypeEnum, RequestHeader } from '@/interfaces/http';
import { RouteDefinition } from '@/interfaces/routes';
@@ -80,7 +81,10 @@ describe('useRequestBuilderStore', () => {
...baseRoute,
method: 'POST',
schema: {
shape: {},
shape: {
'x-name': 'root',
'x-required': false,
},
extractionErrors: null,
},
};
@@ -120,7 +124,10 @@ describe('useRequestBuilderStore', () => {
GET: { [RequestBodyTypeEnum.JSON]: '{}' },
};
const params = [{ key: 'page', value: '1' }];
const auth = { type: AuthorizationType.Bearer, value: 'token' };
const auth: AuthorizationContract = {
type: AuthorizationType.Bearer,
value: 'token',
};
store.updateRequestHeaders(headers);
store.updateRequestBody(body);

View File

@@ -35,7 +35,9 @@ describe('useSettingsStore', () => {
await nextTick();
expect(JSON.parse(window.localStorage.getItem(STORAGE_KEY)).theme).toBe('dark');
expect(JSON.parse(window.localStorage?.getItem(STORAGE_KEY) ?? '{}').theme).toBe(
'dark',
);
});
it('resets preferences to defaults', async () => {

View File

@@ -1,5 +1,6 @@
import { ValueGenerator } from '@/interfaces/ui';
import { useValueGeneratorStore } from '@/stores/generators/useValueGeneratorStore';
import { Mock } from '@vitest/spy';
import { createPinia, setActivePinia } from 'pinia';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { computed, reactive, ref } from 'vue';
@@ -87,7 +88,7 @@ describe('useValueGeneratorStore', () => {
commandStore.addToRecentGenerators.mockClear();
setSearchQuery.mockClear();
setSelectedCategory.mockClear();
generators.forEach(generator => (generator.generate as vi.Mock).mockClear());
generators.forEach(generator => (generator.generate as Mock).mockClear());
});
it('generates value and records generator usage', () => {

View File

@@ -60,10 +60,10 @@ describe('generateCurlCommand', () => {
});
it('flags special authorization types', () => {
const request = {
const request: PendingRequest = {
...requestBase,
authorization: { type: AuthorizationType.Impersonate, value: 1 },
};
} as PendingRequest;
const { hasSpecialAuth } = generateCurlCommand(
request,