Skip to content
This repository was archived by the owner on May 10, 2021. It is now read-only.

Commit 2d30782

Browse files
committed
Add tests for API routes
Test NextJS' API routes: https://nextjs.org/docs/api-routes/introduction
1 parent e0b2a12 commit 2d30782

File tree

9 files changed

+264
-0
lines changed

9 files changed

+264
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import fetch from 'isomorphic-unfetch'
2+
3+
export default async (req, res) => {
4+
// Get the params and query string parameters
5+
const { query } = req
6+
const { params, ...queryStringParams } = query
7+
8+
// Get the ID of the show
9+
const id = params[0]
10+
11+
// Get the data
12+
const fetchRes = await fetch(`https://api.tvmaze.com/shows/${id}`);
13+
const data = await fetchRes.json();
14+
15+
// If show was found, return it
16+
if(fetchRes.status == 200) {
17+
res.status(200)
18+
res.json({ params, queryStringParams, show: data })
19+
}
20+
// If show was not found, return error
21+
else {
22+
res.status(404)
23+
res.json({ error: "Show not found" })
24+
}
25+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import fetch from 'isomorphic-unfetch'
2+
3+
export default async (req, res) => {
4+
// Get the ID of the show
5+
const { query } = req
6+
const { id } = query
7+
8+
// Get the data
9+
const fetchRes = await fetch(`https://api.tvmaze.com/shows/${id}`);
10+
const data = await fetchRes.json();
11+
12+
// If show was found, return it
13+
if(fetchRes.status == 200) {
14+
res.status(200)
15+
res.json({ show: data })
16+
}
17+
// If show was not found, return error
18+
else {
19+
res.status(404)
20+
res.json({ error: "Show not found" })
21+
}
22+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default (req, res) => {
2+
// We can set custom headers
3+
res.setHeader('My-Custom-Header', 'header123')
4+
5+
res.status(200)
6+
res.json({ message: 'hello world :)' })
7+
}

cypress/integration/default_spec.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,56 @@ describe('dynamic catch-all SSR page', () => {
120120
})
121121
})
122122

123+
describe('API endpoint', () => {
124+
it('returns hello world, with all response headers', () => {
125+
cy.request('/api/static').then(response => {
126+
expect(response.headers['content-type']).to.include('application/json')
127+
expect(response.headers['my-custom-header']).to.include('header123')
128+
129+
expect(response.body).to.have.property('message', 'hello world :)')
130+
})
131+
})
132+
})
133+
134+
describe('dynamic API endpoint', () => {
135+
it('returns TV show', () => {
136+
cy.request('/api/shows/305').then(response => {
137+
expect(response.headers['content-type']).to.include('application/json')
138+
139+
expect(response.body).to.have.property('show')
140+
expect(response.body.show).to.have.property('id', 305)
141+
expect(response.body.show).to.have.property('name', 'Black Mirror')
142+
})
143+
})
144+
})
145+
146+
describe('catch-all API endpoint', () => {
147+
it('returns all URL paremeters, including query string parameters', () => {
148+
cy.request('/api/shows/590/this/path/is/captured?metric=dog&p2=cat')
149+
.then(response => {
150+
expect(response.headers['content-type']).to.include('application/json')
151+
152+
// Params
153+
expect(response.body).to.have.property('params')
154+
expect(response.body.params).to.deep.eq([
155+
'590', 'this', 'path', 'is', 'captured'
156+
])
157+
158+
// Query string parameters
159+
expect(response.body).to.have.property('queryStringParams')
160+
expect(response.body.queryStringParams).to.deep.eq({
161+
metric: 'dog',
162+
p2: 'cat'
163+
})
164+
165+
// Show
166+
expect(response.body).to.have.property('show')
167+
expect(response.body.show).to.have.property('id', 590)
168+
expect(response.body.show).to.have.property('name', 'Pokémon')
169+
})
170+
})
171+
})
172+
123173
describe('static page', () => {
124174
it('renders', () => {
125175
cy.visit('/static')

tests/customNextDistDir.test.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,54 @@ describe('SSR Pages', () => {
105105
})
106106
})
107107

108+
describe('API Pages', () => {
109+
const router = join(PROJECT_PATH, "functions", "nextRouter")
110+
111+
test('creates nextRouter.js Netlify Function', () => {
112+
expect(existsSync(join(router, "nextRouter.js"))).toBe(true)
113+
})
114+
115+
test('lists all routes in routes.json', () => {
116+
// read routes
117+
const { routes } = readJsonSync(join(router, "routes.json"))
118+
119+
// check entries
120+
expect(routes).toContainEqual({
121+
file: "pages/api/static.js",
122+
regex: "^\\/api\\/static(?:\\/)?$"
123+
})
124+
expect(routes).toContainEqual({
125+
file: "pages/api/shows/[id].js",
126+
regex: "^\\/api\\/shows\\/([^\\/]+?)(?:\\/)?$"
127+
})
128+
expect(routes).toContainEqual({
129+
file: "pages/api/shows/[...params].js",
130+
regex: "^\\/api\\/shows(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$"
131+
})
132+
})
133+
134+
test('requires all pages in allPages.js', () => {
135+
// read allPages.js
136+
const contents = readFileSync(join(router, "allPages.js"))
137+
138+
// Convert contents into an array, each line being one element
139+
const requires = contents.toString().split("\n")
140+
141+
// Verify presence of require statements
142+
expect(requires).toContain('require("./pages/api/static.js")')
143+
expect(requires).toContain('require("./pages/api/shows/[id].js")')
144+
expect(requires).toContain('require("./pages/api/shows/[...params].js")')
145+
})
146+
147+
test('bundles all SSR-pages in /pages', () => {
148+
const pages = join(PROJECT_PATH, "public", "_next", "pages")
149+
150+
expect(existsSync(join(router, "pages", "api", "static.js"))).toBe(true)
151+
expect(existsSync(join(router, "pages", "api", "shows", "[id].js"))).toBe(true)
152+
expect(existsSync(join(router, "pages", "api", "shows", "[...params].js"))).toBe(true)
153+
})
154+
})
155+
108156
describe('Static Pages', () => {
109157
test('copies static pages to public/_next/ directory', () => {
110158
const pages = join(PROJECT_PATH, "public", "_next", "pages")
@@ -137,5 +185,8 @@ describe('Routing',() => {
137185
expect(redirects).toContain("/index /.netlify/functions/nextRouter 200")
138186
expect(redirects).toContain("/shows/:id /.netlify/functions/nextRouter 200")
139187
expect(redirects).toContain("/shows/* /.netlify/functions/nextRouter 200")
188+
expect(redirects).toContain("/api/static /.netlify/functions/nextRouter 200")
189+
expect(redirects).toContain("/api/shows/:id /.netlify/functions/nextRouter 200")
190+
expect(redirects).toContain("/api/shows/* /.netlify/functions/nextRouter 200")
140191
})
141192
})

tests/defaults.test.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,54 @@ describe('SSR Pages', () => {
105105
})
106106
})
107107

108+
describe('API Pages', () => {
109+
const router = join(PROJECT_PATH, "functions", "nextRouter")
110+
111+
test('creates nextRouter.js Netlify Function', () => {
112+
expect(existsSync(join(router, "nextRouter.js"))).toBe(true)
113+
})
114+
115+
test('lists all routes in routes.json', () => {
116+
// read routes
117+
const { routes } = readJsonSync(join(router, "routes.json"))
118+
119+
// check entries
120+
expect(routes).toContainEqual({
121+
file: "pages/api/static.js",
122+
regex: "^\\/api\\/static(?:\\/)?$"
123+
})
124+
expect(routes).toContainEqual({
125+
file: "pages/api/shows/[id].js",
126+
regex: "^\\/api\\/shows\\/([^\\/]+?)(?:\\/)?$"
127+
})
128+
expect(routes).toContainEqual({
129+
file: "pages/api/shows/[...params].js",
130+
regex: "^\\/api\\/shows(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$"
131+
})
132+
})
133+
134+
test('requires all pages in allPages.js', () => {
135+
// read allPages.js
136+
const contents = readFileSync(join(router, "allPages.js"))
137+
138+
// Convert contents into an array, each line being one element
139+
const requires = contents.toString().split("\n")
140+
141+
// Verify presence of require statements
142+
expect(requires).toContain('require("./pages/api/static.js")')
143+
expect(requires).toContain('require("./pages/api/shows/[id].js")')
144+
expect(requires).toContain('require("./pages/api/shows/[...params].js")')
145+
})
146+
147+
test('bundles all SSR-pages in /pages', () => {
148+
const pages = join(PROJECT_PATH, "public", "_next", "pages")
149+
150+
expect(existsSync(join(router, "pages", "api", "static.js"))).toBe(true)
151+
expect(existsSync(join(router, "pages", "api", "shows", "[id].js"))).toBe(true)
152+
expect(existsSync(join(router, "pages", "api", "shows", "[...params].js"))).toBe(true)
153+
})
154+
})
155+
108156
describe('Static Pages', () => {
109157
test('copies static pages to public/_next/ directory', () => {
110158
const pages = join(PROJECT_PATH, "public", "_next", "pages")
@@ -137,5 +185,8 @@ describe('Routing',() => {
137185
expect(redirects).toContain("/index /.netlify/functions/nextRouter 200")
138186
expect(redirects).toContain("/shows/:id /.netlify/functions/nextRouter 200")
139187
expect(redirects).toContain("/shows/* /.netlify/functions/nextRouter 200")
188+
expect(redirects).toContain("/api/static /.netlify/functions/nextRouter 200")
189+
expect(redirects).toContain("/api/shows/:id /.netlify/functions/nextRouter 200")
190+
expect(redirects).toContain("/api/shows/* /.netlify/functions/nextRouter 200")
140191
})
141192
})
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import fetch from 'isomorphic-unfetch'
2+
3+
export default async (req, res) => {
4+
// Respond with JSON
5+
res.setHeader('Content-Type', 'application/json')
6+
7+
// Get the params and query string parameters
8+
const { query } = req
9+
const { params, ...queryStringParams } = query
10+
11+
// Get the ID of the show
12+
const id = params[0]
13+
14+
// Get the data
15+
const fetchRes = await fetch(`https://api.tvmaze.com/shows/${id}`);
16+
const data = await fetchRes.json();
17+
18+
// If show was found, return it
19+
if(fetchRes.status == 200) {
20+
res.status(200)
21+
res.json({ params, queryStringParams, show: data })
22+
}
23+
// If show was not found, return error
24+
else {
25+
res.status(404)
26+
res.json({ error: "Show not found" })
27+
}
28+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import fetch from 'isomorphic-unfetch'
2+
3+
export default async (req, res) => {
4+
// Respond with JSON
5+
res.setHeader('Content-Type', 'application/json')
6+
7+
// Get the ID of the show
8+
const { query } = req
9+
const { id } = query
10+
11+
// Get the data
12+
const fetchRes = await fetch(`https://api.tvmaze.com/shows/${id}`);
13+
const data = await fetchRes.json();
14+
15+
// If show was found, return it
16+
if(fetchRes.status == 200) {
17+
res.status(200)
18+
res.json({ show: data })
19+
}
20+
// If show was not found, return error
21+
else {
22+
res.status(404)
23+
res.json({ error: "Show not found" })
24+
}
25+
}

tests/fixtures/pages/api/static.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default (req, res) => {
2+
res.setHeader('Content-Type', 'application/json')
3+
res.status(200)
4+
res.json({ message: 'hello world :)' })
5+
}

0 commit comments

Comments
 (0)