Skip to content

Commit 85ff882

Browse files
authored
Reduce explicit any usage across automated contexts and tests (#58806)
1 parent ad0791e commit 85ff882

File tree

11 files changed

+216
-114
lines changed

11 files changed

+216
-114
lines changed

eslint.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export default [
9797

9898
// Disabled rules to review
9999
'no-console': 'off', // 800+
100-
'@typescript-eslint/no-explicit-any': 'off', // 1000+
100+
'@typescript-eslint/no-explicit-any': 'off',
101101
},
102102
},
103103

src/automated-pipelines/components/AutomatedPageContext.tsx

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { createContext, useContext } from 'react'
2+
import type { IncomingMessage } from 'http'
23
import type { JSX } from 'react'
34
import type { MiniTocItem } from '@/frame/components/context/ArticleContext'
5+
import type { Context } from '@/types'
46

57
export type AutomatedPageContextT = {
68
title: string
@@ -25,15 +27,35 @@ export const useAutomatedPageContext = (): AutomatedPageContextT => {
2527
return context
2628
}
2729

28-
export const getAutomatedPageContextFromRequest = (req: any): AutomatedPageContextT => {
29-
const page = req.context.page
30+
type AutomatedPageContextRequest = { context?: Partial<Context> } | IncomingMessage
31+
32+
type AutomatedPage = {
33+
title: string
34+
intro: string
35+
product?: string
36+
permissions?: string
37+
rawPermissions?: string
38+
}
39+
40+
export const getAutomatedPageContextFromRequest = (
41+
req: AutomatedPageContextRequest,
42+
): AutomatedPageContextT => {
43+
const context = 'context' in req ? ((req.context as Partial<Context> | undefined) ?? {}) : {}
44+
const page = context.page as AutomatedPage | undefined
45+
46+
if (!page) {
47+
throw new Error('"getAutomatedPageContextFromRequest" requires req.context.page')
48+
}
49+
50+
const renderedPage = context.renderedPage ?? ''
51+
const miniTocItems = context.miniTocItems ?? []
3052

3153
return {
3254
title: page.title,
3355
intro: page.intro,
34-
renderedPage: req.context.renderedPage || '',
35-
miniTocItems: req.context.miniTocItems || [],
36-
product: page.product || '',
37-
permissions: page.permissions || '',
56+
renderedPage,
57+
miniTocItems,
58+
product: page.product ?? '',
59+
permissions: page.permissions ?? page.rawPermissions ?? '',
3860
}
3961
}

src/fixtures/tests/annotations.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('annotations', () => {
2222
expect(annotation.find('.annotate-inline').length).toBe(1)
2323
expect(annotation.find('.annotate-row').length).toBe(3)
2424
const notes = $('.annotate-row .annotate-note p', annotation)
25-
const noteTexts = notes.map((i: number, el: any) => $(el).text()).get()
25+
const noteTexts = notes.map((_, el) => $(el).text()).get()
2626
expect(noteTexts).toEqual(["Let's get started", 'This is just a sample', 'End of the script'])
2727
}
2828
// Second code snippet block
@@ -33,7 +33,7 @@ describe('annotations', () => {
3333
expect(annotation.find('.annotate-inline').length).toBe(1)
3434
expect(annotation.find('.annotate-row').length).toBe(2)
3535
const notes = $('.annotate-row .annotate-note p', annotation)
36-
const noteTexts = notes.map((i: number, el: any) => $(el).text()).get()
36+
const noteTexts = notes.map((_, el) => $(el).text()).get()
3737
expect(noteTexts).toEqual(['Has to start with a comment.', 'This is the if statement'])
3838
}
3939
// Yaml code snippet that starts with an empty comment
@@ -44,7 +44,7 @@ describe('annotations', () => {
4444
expect(annotation.find('.annotate-inline').length).toBe(1)
4545
expect(annotation.find('.annotate-row').length).toBe(3)
4646
const notes = $('.annotate-row .annotate-note p', annotation)
47-
const noteTexts = notes.map((i: number, el: any) => $(el).text()).get()
47+
const noteTexts = notes.map((_, el) => $(el).text()).get()
4848
expect(noteTexts).toEqual([
4949
'Configures this workflow to run every time a change is pushed to the branch called release.',
5050
"This job checks out the repository contents ...\nAnd here's the second comment line.",

src/frame/tests/find-page-middleware.ts

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,52 @@
11
import { fileURLToPath } from 'url'
22
import path from 'path'
33
import http from 'http'
4+
import { Socket } from 'net'
45

56
import { describe, expect, test } from 'vitest'
67
import type { Response } from 'express'
78

89
import Page from '@/frame/lib/page'
910
import findPage from '@/frame/middleware/find-page'
10-
import type { ExtendedRequest } from '@/types'
11+
import type { ExtendedRequest, Context } from '@/types'
1112

1213
const __dirname = path.dirname(fileURLToPath(import.meta.url))
1314

15+
type TestResponse = Response & { _status?: number; _message?: string }
16+
1417
function makeRequestResponse(
1518
url: string,
1619
currentVersion = 'free-pro-team@latest',
17-
): [ExtendedRequest, Response & { _status?: number; _message?: string }] {
18-
const req = new http.IncomingMessage(null as any) as ExtendedRequest
20+
): [ExtendedRequest, TestResponse] {
21+
const req = new http.IncomingMessage(new Socket()) as ExtendedRequest
22+
23+
Object.defineProperty(req, 'path', {
24+
value: url,
25+
writable: true,
26+
})
27+
1928
req.method = 'GET'
2029
req.url = url
21-
// @ts-expect-error - path is read-only but we need to set it for testing
22-
req.path = url
2330
req.cookies = {}
2431
req.headers = {}
2532

26-
// Custom keys on the request
33+
const context: Context = {
34+
currentVersion,
35+
pages: {},
36+
}
37+
2738
req.pagePath = url
28-
req.context = {}
29-
req.context.currentVersion = currentVersion
30-
req.context.pages = {}
39+
req.context = context
3140

32-
const res = new http.ServerResponse(req) as Response & {
33-
_status?: number
34-
_message?: string
35-
}
36-
res.status = function (code: number) {
41+
const res = new http.ServerResponse(req) as TestResponse
42+
res.status = function status(this: TestResponse, code: number) {
3743
this._status = code
38-
return {
44+
return Object.assign(this, {
3945
send: (message: string) => {
4046
this._message = message
47+
return this
4148
},
42-
} as any
49+
})
4350
}
4451

4552
return [req, res]
@@ -56,7 +63,7 @@ describe('find page middleware', () => {
5663
})
5764
if (page && req.context) {
5865
req.context.pages = {
59-
'/en/foo/bar': page as any,
66+
'/en/foo/bar': page,
6067
}
6168
}
6269

@@ -88,7 +95,7 @@ describe('find page middleware', () => {
8895
})
8996
if (page && req.context) {
9097
req.context.pages = {
91-
'/en/page-with-redirects': page as any,
98+
'/en/page-with-redirects': page,
9299
}
93100
}
94101

@@ -98,6 +105,7 @@ describe('find page middleware', () => {
98105
})
99106
expect(req.context?.page).toBeInstanceOf(Page)
100107
})
108+
101109
test('finds it for non-fpt version URLS', async () => {
102110
const [req, res] = makeRequestResponse('/en/page-with-redirects', 'enterprise-cloud@latest')
103111
const page = await Page.init({
@@ -107,7 +115,7 @@ describe('find page middleware', () => {
107115
})
108116
if (page && req.context) {
109117
req.context.pages = {
110-
'/en/page-with-redirects': page as any,
118+
'/en/page-with-redirects': page,
111119
}
112120
}
113121

@@ -129,7 +137,7 @@ describe('find page middleware', () => {
129137
})
130138
if (page && req.context) {
131139
req.context.pages = {
132-
'/en/page-with-redirects': page as any,
140+
'/en/page-with-redirects': page,
133141
}
134142
}
135143

src/frame/tests/resolve-recommended.test.ts

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, test, expect, vi, beforeEach } from 'vitest'
22
import type { Response, NextFunction } from 'express'
3-
import type { ExtendedRequest } from '@/types'
3+
import type { ExtendedRequest, Page, ResolvedArticle } from '@/types'
44
import findPage from '@/frame/lib/find-page'
55
import resolveRecommended from '../middleware/resolve-recommended'
66

@@ -17,23 +17,38 @@ vi.mock('@/content-render/index', () => ({
1717
describe('resolveRecommended middleware', () => {
1818
const mockFindPage = vi.mocked(findPage)
1919

20-
const createMockRequest = (pageData: any = {}, contextData: any = {}): ExtendedRequest =>
21-
({
22-
context: {
23-
page: pageData,
24-
pages: {
20+
type TestPage = Partial<Page> & {
21+
rawRecommended?: string[]
22+
spotlight?: Array<{ article: string }>
23+
}
24+
25+
const createMockRequest = (
26+
pageData: TestPage = {},
27+
contextData: Partial<ExtendedRequest['context']> & { pages?: Record<string, Page> } = {},
28+
): ExtendedRequest => {
29+
const { pages: pagesOverride, ...restContext } = contextData
30+
const hasPagesOverride = Object.prototype.hasOwnProperty.call(contextData, 'pages')
31+
const pages = hasPagesOverride
32+
? pagesOverride
33+
: ({
2534
'/test-article': {
2635
title: 'Test Article',
2736
intro: 'Test intro',
2837
relativePath: 'test/article.md',
29-
},
30-
},
38+
} as unknown as Page,
39+
} as Record<string, Page>)
40+
41+
return {
42+
context: {
43+
page: pageData as Page,
44+
pages,
3145
redirects: {},
3246
currentVersion: 'free-pro-team@latest',
3347
currentLanguage: 'en',
34-
...contextData,
48+
...restContext,
3549
},
36-
}) as ExtendedRequest
50+
} as unknown as ExtendedRequest
51+
}
3752

3853
const mockRes = {} as Response
3954
const mockNext = vi.fn() as NextFunction
@@ -86,7 +101,7 @@ describe('resolveRecommended middleware', () => {
86101
applicableVersions: ['free-pro-team@latest'],
87102
}
88103

89-
mockFindPage.mockReturnValue(testPage as any)
104+
mockFindPage.mockReturnValue(testPage as unknown as Page)
90105

91106
const req = createMockRequest({ rawRecommended: ['/copilot/tutorials/article'] })
92107

@@ -97,7 +112,7 @@ describe('resolveRecommended middleware', () => {
97112
req.context!.pages,
98113
req.context!.redirects,
99114
)
100-
expect((req.context!.page as any).recommended).toEqual([
115+
expect((req.context!.page as Page & { recommended?: ResolvedArticle[] }).recommended).toEqual([
101116
{
102117
title: 'Test Article',
103118
intro: '<p>Test intro</p>',
@@ -116,7 +131,7 @@ describe('resolveRecommended middleware', () => {
116131
applicableVersions: ['free-pro-team@latest'],
117132
}
118133

119-
mockFindPage.mockReturnValueOnce(testPage as any)
134+
mockFindPage.mockReturnValueOnce(testPage as unknown as Page)
120135

121136
const req = createMockRequest({
122137
rawRecommended: ['/copilot/tutorials/article'],
@@ -131,7 +146,7 @@ describe('resolveRecommended middleware', () => {
131146
req.context!.pages,
132147
req.context!.redirects,
133148
)
134-
expect((req.context!.page as any).recommended).toEqual([
149+
expect((req.context!.page as Page & { recommended?: ResolvedArticle[] }).recommended).toEqual([
135150
{
136151
title: 'Test Article',
137152
intro: '<p>Test intro</p>',
@@ -154,7 +169,9 @@ describe('resolveRecommended middleware', () => {
154169
req.context!.pages,
155170
req.context!.redirects,
156171
)
157-
expect((req.context!.page as any).recommended).toEqual([])
172+
expect((req.context!.page as Page & { recommended?: ResolvedArticle[] }).recommended).toEqual(
173+
[],
174+
)
158175
expect(mockNext).toHaveBeenCalled()
159176
})
160177

@@ -163,11 +180,13 @@ describe('resolveRecommended middleware', () => {
163180
throw new Error('Test error')
164181
})
165182

166-
const req = createMockRequest({ rawRecommended: ['/error-article'] })
183+
const req = createMockRequest({ rawRecommended: ['/error-article'] as string[] })
167184

168185
await resolveRecommended(req, mockRes, mockNext)
169186

170-
expect((req.context!.page as any).recommended).toEqual([])
187+
expect((req.context!.page as Page & { recommended?: ResolvedArticle[] }).recommended).toEqual(
188+
[],
189+
)
171190
expect(mockNext).toHaveBeenCalled()
172191
})
173192

@@ -179,13 +198,13 @@ describe('resolveRecommended middleware', () => {
179198
applicableVersions: ['free-pro-team@latest'],
180199
}
181200

182-
mockFindPage.mockReturnValueOnce(testPage as any).mockReturnValueOnce(undefined)
201+
mockFindPage.mockReturnValueOnce(testPage as unknown as Page).mockReturnValueOnce(undefined)
183202

184203
const req = createMockRequest({ rawRecommended: ['/valid-article', '/invalid-article'] })
185204

186205
await resolveRecommended(req, mockRes, mockNext)
187206

188-
expect((req.context!.page as any).recommended).toEqual([
207+
expect((req.context!.page as Page & { recommended?: ResolvedArticle[] }).recommended).toEqual([
189208
{
190209
title: 'Valid Article',
191210
intro: '<p>Valid intro</p>',
@@ -205,7 +224,7 @@ describe('resolveRecommended middleware', () => {
205224
}
206225

207226
// Mock findPage to fail on first call (content-relative) and succeed on second (page-relative)
208-
mockFindPage.mockReturnValueOnce(undefined).mockReturnValueOnce(testPage as any)
227+
mockFindPage.mockReturnValueOnce(undefined).mockReturnValueOnce(testPage as unknown as Page)
209228

210229
const req = createMockRequest({
211230
rawRecommended: ['relative-article'],
@@ -225,7 +244,7 @@ describe('resolveRecommended middleware', () => {
225244
req.context!.pages,
226245
req.context!.redirects,
227246
)
228-
expect((req.context!.page as any).recommended).toEqual([
247+
expect((req.context!.page as Page & { recommended?: ResolvedArticle[] }).recommended).toEqual([
229248
{
230249
title: 'Relative Article',
231250
intro: '<p>Relative intro</p>',
@@ -244,7 +263,7 @@ describe('resolveRecommended middleware', () => {
244263
applicableVersions: ['free-pro-team@latest'],
245264
}
246265

247-
mockFindPage.mockReturnValue(testPage as any)
266+
mockFindPage.mockReturnValue(testPage as unknown as Page)
248267

249268
const req = createMockRequest({ rawRecommended: ['/copilot/tutorials/tutorial-page'] })
250269

@@ -258,7 +277,7 @@ describe('resolveRecommended middleware', () => {
258277

259278
// Verify that the href is a clean path without language/version, that gets
260279
// added on the React side.
261-
expect((req.context!.page as any).recommended).toEqual([
280+
expect((req.context!.page as Page & { recommended?: ResolvedArticle[] }).recommended).toEqual([
262281
{
263282
title: 'Tutorial Page',
264283
intro: '<p>Tutorial intro</p>',
@@ -278,7 +297,7 @@ describe('resolveRecommended middleware', () => {
278297
applicableVersions: ['free-pro-team@latest'], // Not available in ghec
279298
}
280299

281-
mockFindPage.mockReturnValue(fptOnlyPage as any)
300+
mockFindPage.mockReturnValue(fptOnlyPage as unknown as Page)
282301

283302
// Create a request context where we're viewing the GHEC version
284303
const req = createMockRequest(
@@ -292,7 +311,9 @@ describe('resolveRecommended middleware', () => {
292311
await resolveRecommended(req, mockRes, mockNext)
293312

294313
// The recommended array should be empty since the article isn't available in enterprise-cloud
295-
expect((req.context!.page as any).recommended).toEqual([])
314+
expect((req.context!.page as Page & { recommended?: ResolvedArticle[] }).recommended).toEqual(
315+
[],
316+
)
296317
expect(mockNext).toHaveBeenCalled()
297318
})
298319
})

0 commit comments

Comments
 (0)