Skip to content

Commit 3a5aa2c

Browse files
committed
test: cover registry auth scoping and dev server responses
1 parent 7ad4e6b commit 3a5aa2c

3 files changed

Lines changed: 74 additions & 12 deletions

File tree

‎packages/nuxt-cli/test/unit/dev/responses.spec.ts‎

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import { describe, expect, it } from 'vitest'
55
import{renderError}from'../../../src/dev/error'
66
import{NuxtDevServer}from'../../../src/dev/utils'
77

8-
interfaceFakeResponseextendsServerResponse{
8+
interfaceFakeResponse{
99
body: string
1010
statusCode: number
11+
headersSent: boolean
1112
headers: Record<string,string>
1213
finished: Promise<void>
14+
response: ServerResponse
1315
}
1416

1517
functioncreateRequest(accept?: string,url='/'): IncomingMessage{
@@ -42,13 +44,17 @@ function createResponse(): FakeResponse {
4244
done()
4345
},
4446
}
45-
returnresasunknownasFakeResponse
47+
returnnewProxy(res,{
48+
get(target,key){
49+
returnkey==='response' ? target : Reflect.get(target,key)
50+
},
51+
})asunknownasFakeResponse
4652
}
4753

4854
describe('renderError',()=>{
4955
it('should escape an error message in the html error page',async()=>{
5056
constres=createResponse()
51-
awaitrenderError(createRequest('text/html'),res,newError('<script>alert(1)</script>'))
57+
awaitrenderError(createRequest('text/html'),res.response,newError('<script>alert(1)</script>'))
5258

5359
expect(res.statusCode).toBe(500)
5460
expect(res.headers['content-type']).toBe('text/html')
@@ -58,22 +64,22 @@ describe('renderError', () => {
5864

5965
it('should escape a reflected request url in the html error page',async()=>{
6066
constres=createResponse()
61-
awaitrenderError(createRequest('text/html','/</script><script>alert(1)</script>'),res,newError('boom'))
67+
awaitrenderError(createRequest('text/html','/</script><script>alert(1)</script>'),res.response,newError('boom'))
6268

6369
expect(res.body).not.toContain('<script>alert(1)</script>')
6470
})
6571

6672
it('should answer a non-html client with json',async()=>{
6773
constres=createResponse()
68-
awaitrenderError(createRequest('application/json'),res,newError('boom'))
74+
awaitrenderError(createRequest('application/json'),res.response,newError('boom'))
6975

7076
expect(res.headers['content-type']).toBe('application/json')
7177
expect(JSON.parse(res.body)).toMatchObject({error: true,status: 500,message: 'boom'})
7278
})
7379

7480
it('should send hardening headers with the error page',async()=>{
7581
constres=createResponse()
76-
awaitrenderError(createRequest('text/html'),res,newError('boom'))
82+
awaitrenderError(createRequest('text/html'),res.response,newError('boom'))
7783

7884
expect(res.headers).toMatchObject({
7985
'cache-control': 'no-store',
@@ -86,14 +92,14 @@ describe('renderError', () => {
8692
it('should not write a body once headers have been sent',async()=>{
8793
constres=createResponse()
8894
res.headersSent=true
89-
awaitrenderError(createRequest('text/html'),res,newError('boom'))
95+
awaitrenderError(createRequest('text/html'),res.response,newError('boom'))
9096

9197
expect(res.body).toBe('')
9298
})
9399

94100
it('should render a non-error rejection value',async()=>{
95101
constres=createResponse()
96-
awaitrenderError(createRequest('application/json'),res,'just a string')
102+
awaitrenderError(createRequest('application/json'),res.response,'just a string')
97103

98104
expect(JSON.parse(res.body)).toMatchObject({status: 500,message: 'Unknown error'})
99105
})
@@ -108,7 +114,7 @@ describe('dev server loading screen', () => {
108114
constserver=createDevServer(({ loading })=>`<p>${loading}</p>`)
109115
constres=createResponse()
110116

111-
server.handler(createRequest('text/html'),res)
117+
server.handler(createRequest('text/html'),resasunknownasServerResponse)
112118
awaitres.finished
113119

114120
expect(res.statusCode).toBe(503)
@@ -120,7 +126,7 @@ describe('dev server loading screen', () => {
120126
constserver=createDevServer(()=>'<p>ignored</p>')
121127
constres=createResponse()
122128

123-
server.handler(createRequest('application/json'),res)
129+
server.handler(createRequest('application/json'),resasunknownasServerResponse)
124130
awaitres.finished
125131

126132
expect(res.statusCode).toBe(503)
@@ -131,7 +137,7 @@ describe('dev server loading screen', () => {
131137
constserver=createDevServer(()=>'loading')
132138
constres=createResponse()
133139

134-
server.handler(createRequest('text/html'),res)
140+
server.handler(createRequest('text/html'),resasunknownasServerResponse)
135141
awaitres.finished
136142

137143
expect(res.headers).toMatchObject({'cache-control': 'no-store','refresh': '3'})

‎packages/nuxt-cli/test/unit/utils/console.spec.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ afterEach(() => {
2525
Object.defineProperty(stdin,key,descriptors[key])
2626
}
2727
else{
28-
delete(stdinasRecord<string,unknown>)[key]
28+
delete(stdinasunknownasRecord<string,unknown>)[key]
2929
}
3030
}
3131
})

‎packages/nuxt-cli/test/unit/utils/registry.spec.ts‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,59 @@ describe('detectNpmRegistry', () => {
8383
expect(process.env.COREPACK_NPM_REGISTRY).toBeUndefined()
8484
})
8585
})
86+
87+
describe('auth token scoping',()=>{
88+
asyncfunctionnpmrc(...lines: string[]): Promise<string>{
89+
constdirectory=awaitmkdtemp(join(tmpdir(),'nuxt-registry-'))
90+
directories.push(directory)
91+
awaitwriteFile(join(directory,'.npmrc'),lines.join('\n'))
92+
returndirectory
93+
}
94+
95+
it('should not return a token registered for a different registry',async()=>{
96+
constdirectory=awaitnpmrc(
97+
'registry=https://registry.example.com/',
98+
'//registry.other.com/:_authToken=secret',
99+
)
100+
101+
awaitexpect(detectNpmRegistry(null,directory)).resolves.toMatchObject({authToken: null})
102+
})
103+
104+
it('should not return a token for a lookalike host',async()=>{
105+
constdirectory=awaitnpmrc(
106+
'registry=https://registry.example.com.evil.test/',
107+
'//registry.example.com/:_authToken=secret',
108+
)
109+
110+
awaitexpect(detectNpmRegistry(null,directory)).resolves.toMatchObject({
111+
registry: 'https://registry.example.com.evil.test',
112+
authToken: null,
113+
})
114+
})
115+
116+
it('should resolve the token of the scoped registry that is actually used',async()=>{
117+
constdirectory=awaitnpmrc(
118+
'registry=https://registry.example.com/',
119+
'@scope:registry=https://scoped.example.com/',
120+
'//registry.example.com/:_authToken=default-token',
121+
'//scoped.example.com/:_authToken=scoped-token',
122+
)
123+
124+
awaitexpect(detectNpmRegistry('@scope',directory)).resolves.toEqual({
125+
registry: 'https://scoped.example.com',
126+
authToken: 'scoped-token',
127+
})
128+
})
129+
130+
it('should prefer COREPACK_NPM_REGISTRY over the project npmrc',async()=>{
131+
constdirectory=awaitnpmrc('registry=https://registry.example.com/')
132+
process.env.COREPACK_NPM_REGISTRY='https://corepack.example.com/'
133+
134+
try{
135+
awaitexpect(detectNpmRegistry(null,directory)).resolves.toMatchObject({registry: 'https://corepack.example.com'})
136+
}
137+
finally{
138+
deleteprocess.env.COREPACK_NPM_REGISTRY
139+
}
140+
})
141+
})

0 commit comments

Comments
 (0)