Skip to content

refactor: replace ava with vitest and esm #5836

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
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
2 changes: 2 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ module.exports = {
files: ['tests/**/*'],
rules: {
'require-await': 'off',
// TODO: Remove rule when ava is totally removed from project
'ava/no-import-test-files': 'off',
},
},
{
Expand Down
14 changes: 0 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@
"test:init:cli-help": "npm run start -- --help",
"test:init:eleventy-deps": "cd tests/integration/__fixtures__/eleventy-site && pnpm install --frozen-lockfile",
"test:init:hugo-deps": "npm ci --prefix tests/integration/__fixtures__/hugo-site --no-audit",
"test:dev:ava": "ava --verbose",
"test:dev:vitest": "vitest run tests/unit/ && vitest run tests/integration",
"test:ci:ava:integration": "c8 -r json ava --concurrency 1 --no-worker-threads tests/integration/",
"test:ci:vitest:unit": "vitest run --coverage tests/unit/",
"test:ci:vitest:integration": "vitest run --coverage tests/integration/",
"test:affected": "node ./tools/affected-test.mjs",
Expand Down Expand Up @@ -216,17 +214,5 @@
"typescript": "5.1.6",
"verdaccio": "5.25.0",
"vitest": "0.33.0"
},
"ava": {
"files": [
"tools/**/*.test.mjs",
"tests/**/*.test.cjs"
],
"cache": true,
"concurrency": 5,
"failFast": false,
"failWithoutAssertions": false,
"tap": false,
"timeout": "5m"
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
// Handlers are meant to be async outside tests
const { promises: fs } = require('fs')
const { join } = require('path')
import fs from 'fs/promises'
import { join } from 'path'

// eslint-disable-next-line ava/use-test
const avaTest = require('ava')
const { isCI } = require('ci-info')
const jwt = require('jsonwebtoken')
const fetch = require('node-fetch')
import jwt from 'jsonwebtoken'
import fetch from 'node-fetch'
import { test } from 'vitest'

const { withDevServer } = require('./utils/dev-server.cjs')
const { startExternalServer } = require('./utils/external-server.cjs')
const got = require('./utils/got.cjs')
const { withMockApi } = require('./utils/mock-api.cjs')
const { withSiteBuilder } = require('./utils/site-builder.cjs')
import { withDevServer } from './utils/dev-server.cjs'
import { startExternalServer } from './utils/external-server.cjs'
import got from './utils/got.cjs'
import { withMockApi } from './utils/mock-api.cjs'
import { withSiteBuilder } from './utils/site-builder.cjs'

const test = isCI ? avaTest.serial.bind(avaTest) : avaTest
// FIXME: Run tests serial
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course. I'll do it!

// const test = isCI ? avaTest.serial.bind(avaTest) : avaTest

test('should return 404.html if exists for non existing routes', async (t) => {
await withSiteBuilder('site-with-shadowing-404', async (builder) => {
Expand All @@ -27,8 +26,8 @@ test('should return 404.html if exists for non existing routes', async (t) => {

await withDevServer({ cwd: builder.directory }, async (server) => {
const response = await got(`${server.url}/non-existent`, { throwHttpErrors: false })
t.is(response.headers.etag, undefined)
t.is(response.body, '<h1>404 - Page not found</h1>')
t.expect(response.headers.etag).toBeUndefined()
t.expect(response.body).toEqual('<h1>404 - Page not found</h1>')
})
})
})
Expand All @@ -52,9 +51,9 @@ test('should return 404.html from publish folder if exists for non existing rout

await withDevServer({ cwd: builder.directory }, async (server) => {
const response = await got(`${server.url}/non-existent`, { throwHttpErrors: false })
t.is(response.statusCode, 404)
t.is(response.headers.etag, undefined)
t.is(response.body, '<h1>404 - My Custom 404 Page</h1>')
t.expect(response.statusCode).toBe(404)
t.expect(response.headers.etag).toBeUndefined()
t.expect(response.body).toEqual('<h1>404 - My Custom 404 Page</h1>')
})
})
})
Expand All @@ -76,9 +75,9 @@ test('should return 404 for redirect', async (t) => {

await withDevServer({ cwd: builder.directory }, async (server) => {
const response = await got(`${server.url}/test-404`, { throwHttpErrors: false })
t.truthy(response.headers.etag)
t.is(response.statusCode, 404)
t.is(response.body, '<html><h1>foo')
t.expect(response.headers.etag).toBeTruthy()
t.expect(response.statusCode).toBe(404)
t.expect(response.body).toEqual('<html><h1>foo')
})
})
})
Expand All @@ -105,8 +104,8 @@ test('should ignore 404 redirect for existing file', async (t) => {
await withDevServer({ cwd: builder.directory }, async (server) => {
const response = await got(`${server.url}/test-404`)

t.is(response.statusCode, 200)
t.is(response.body, '<html><h1>This page actually exists')
t.expect(response.statusCode).toBe(200)
t.expect(response.body).toEqual('<html><h1>This page actually exists')
})
})
})
Expand All @@ -133,8 +132,8 @@ test('should follow 404 redirect even with existing file when force=true', async
await withDevServer({ cwd: builder.directory }, async (server) => {
const response = await got(`${server.url}/test-404`, { throwHttpErrors: false })

t.is(response.statusCode, 404)
t.is(response.body, '<html><h1>foo')
t.expect(response.statusCode).toBe(404)
t.expect(response.body).toEqual('<html><h1>foo')
})
})
})
Expand All @@ -161,8 +160,8 @@ test('should source redirects file from publish directory', async (t) => {
await withDevServer({ cwd: builder.directory }, async (server) => {
const response = await got(`${server.url}/test`)

t.is(response.statusCode, 200)
t.is(response.body, 'index')
t.expect(response.statusCode).toBe(200)
t.expect(response.body).toEqual('index')
})
})
})
Expand All @@ -179,9 +178,9 @@ test('should rewrite requests to an external server', async (t) => {

await withDevServer({ cwd: builder.directory }, async (server) => {
const getResponse = await got(`${server.url}/api/ping`).json()
t.deepEqual(getResponse.body, {})
t.is(getResponse.method, 'GET')
t.is(getResponse.url, '/ping')
t.expect(getResponse.body).toStrictEqual({})
t.expect(getResponse.method).toEqual('GET')
t.expect(getResponse.url).toEqual('/ping')

const postResponse = await got
.post(`${server.url}/api/ping`, {
Expand All @@ -192,9 +191,9 @@ test('should rewrite requests to an external server', async (t) => {
followRedirect: false,
})
.json()
t.deepEqual(postResponse.body, { param: 'value' })
t.is(postResponse.method, 'POST')
t.is(postResponse.url, '/ping')
t.expect(postResponse.body).toStrictEqual({ param: 'value' })
t.expect(postResponse.method).toEqual('POST')
t.expect(postResponse.url).toEqual('/ping')
})

externalServer.close()
Expand Down Expand Up @@ -259,13 +258,13 @@ test('should sign external redirects with the `x-nf-sign` header when a `signed`
const signature = response.headers['x-nf-sign']
const payload = jwt.verify(signature, mockSigningSecret)

t.is(payload.deploy_context, 'dev')
t.is(payload.netlify_id, siteInfo.id)
t.is(payload.site_url, siteInfo.url)
t.is(payload.iss, 'netlify')
t.expect(payload.deploy_context).toEqual('dev')
t.expect(payload.netlify_id).toEqual(siteInfo.id)
t.expect(payload.site_url).toEqual(siteInfo.url)
t.expect(payload.iss).toEqual('netlify')
})

t.deepEqual(postResponse.body, { param: 'value' })
t.expect(postResponse.body).toStrictEqual({ param: 'value' })
},
)
})
Expand All @@ -286,12 +285,12 @@ test('should follow 301 redirect to an external server', async (t) => {

await withDevServer({ cwd: builder.directory }, async (server) => {
const response1 = await got(`${server.url}/api/ping`, { followRedirect: false })
t.is(response1.headers.location, `http://localhost:${port}/ping`)
t.expect(response1.headers.location).toEqual(`http://localhost:${port}/ping`)

const response2 = await got(`${server.url}/api/ping`).json()
t.deepEqual(response2.body, {})
t.is(response2.method, 'GET')
t.is(response2.url, '/ping')
t.expect(response2.body).toStrictEqual({})
t.expect(response2.method).toEqual('GET')
t.expect(response2.url).toEqual('/ping')
})

externalServer.close()
Expand All @@ -310,16 +309,15 @@ test('should rewrite POST request if content-type is missing and not crash dev s
await builder.buildAsync()

await withDevServer({ cwd: builder.directory }, async (server) => {
const error = await t.throwsAsync(
async () =>
await got.post(`${server.url}/api/echo`, {
body: 'param=value',
followRedirect: false,
}),
)
const error = await got
.post(`${server.url}/api/echo`, {
body: 'param=value',
followRedirect: false,
})
.catch((error_) => error_)

// Method Not Allowed
t.is(error.response.statusCode, 405)
t.expect(error.response.statusCode).toBe(405)
})
})
})
Expand All @@ -341,8 +339,8 @@ test('should return .html file when file and folder have the same name', async (
await withDevServer({ cwd: builder.directory }, async (server) => {
const response = await got(`${server.url}/foo`)

t.is(response.statusCode, 200)
t.is(response.body, '<html><h1>foo')
t.expect(response.statusCode).toBe(200)
t.expect(response.body).toEqual('<html><h1>foo')
})
})
})
Expand Down Expand Up @@ -377,8 +375,8 @@ test('should not shadow an existing file that has unsafe URL characters', async
got(`${server.url}/files/[file_with_brackets]`).text(),
])

t.is(spaces, '<html>file with spaces</html>')
t.is(brackets, '<html>file with brackets</html>')
t.expect(spaces).toEqual('<html>file with spaces</html>')
t.expect(brackets).toEqual('<html>file with brackets</html>')
})
})
})
Expand All @@ -403,28 +401,28 @@ test('should generate an ETag for static assets', async (t) => {
const res1 = await fetch(`${server.url}`)
const etag = res1.headers.get('etag')

t.truthy(etag)
t.is(res1.status, 200)
t.truthy(await res1.text())
t.expect(etag).toBeTruthy()
t.expect(res1.status).toBe(200)
await t.expect(await res1.text()).toBeTruthy()

const res2 = await fetch(`${server.url}`, {
headers: {
'if-none-match': etag,
},
})

t.is(res2.status, 304)
t.falsy(await res2.text())
t.expect(res2.status).toBe(304)
t.expect(await res2.text()).toBeFalsy()

const res3 = await fetch(`${server.url}`, {
headers: {
'if-none-match': 'stale-etag',
},
})

t.truthy(res3.headers.get('etag'))
t.is(res3.status, 200)
t.truthy(await res3.text())
t.expect(res3.headers.get('etag')).toBeTruthy()
t.expect(res3.status).toBe(200)
await t.expect(await res3.text()).toBeTruthy()
})
})
})
Expand All @@ -448,7 +446,7 @@ test('should add `.netlify` to an existing `.gitignore` file', async (t) => {
const gitignore = await fs.readFile(join(builder.directory, '.gitignore'), 'utf8')
const entries = gitignore.split('\n')

t.true(entries.includes('.netlify'))
t.expect(entries.includes('.netlify')).toBe(true)
})
})
})
Expand All @@ -466,7 +464,7 @@ test('should create a `.gitignore` file with `.netlify`', async (t) => {
const gitignore = await fs.readFile(join(builder.directory, '.gitignore'), 'utf8')
const entries = gitignore.split('\n')

t.true(entries.includes('.netlify'))
t.expect(entries.includes('.netlify')).toBe(true)
})
})
})
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const test = require('ava')
import { test } from 'vitest'

const callCli = require('./utils/call-cli.cjs')
const { getCLIOptions, withMockApi } = require('./utils/mock-api.cjs')
const { withSiteBuilder } = require('./utils/site-builder.cjs')
import callCli from './utils/call-cli.cjs'
import { getCLIOptions, withMockApi } from './utils/mock-api.cjs'
import { withSiteBuilder } from './utils/site-builder.cjs'

const siteInfo = {
account_slug: 'test-account',
Expand All @@ -24,7 +24,7 @@ test('netlify addons:list', async (t) => {

await withMockApi(routes, async ({ apiUrl }) => {
const cliResponse = await callCli(['addons:list'], getCLIOptions({ builder, apiUrl }))
t.true(cliResponse.includes('No addons currently installed'))
t.expect(cliResponse.includes('No addons currently installed')).toBe(true)
})
})
})
Expand All @@ -36,8 +36,8 @@ test('netlify addons:list --json', async (t) => {
await withMockApi(routes, async ({ apiUrl }) => {
const cliResponse = await callCli(['addons:list', '--json'], getCLIOptions({ builder, apiUrl }))
const json = JSON.parse(cliResponse)
t.true(Array.isArray(json))
t.is(json.length, 0)
t.expect(Array.isArray(json)).toBe(true)
t.expect(json.length).toBe(0)
})
})
})
Expand All @@ -62,7 +62,7 @@ test('netlify addons:create demo', async (t) => {
['addons:create', 'demo', '--TWILIO_ACCOUNT_SID', 'foo'],
getCLIOptions({ builder, apiUrl }),
)
t.true(cliResponse.includes('Add-on "demo" created'))
t.expect(cliResponse.includes('Add-on "demo" created')).toBe(true)
})
})
})
Expand All @@ -86,9 +86,9 @@ test('After creation netlify addons:list --json', async (t) => {
await withMockApi(withExistingAddon, async ({ apiUrl }) => {
const cliResponse = await callCli(['addons:list', '--json'], getCLIOptions({ builder, apiUrl }))
const json = JSON.parse(cliResponse)
t.true(Array.isArray(json))
t.is(json.length, 1)
t.is(json[0].service_slug, 'demo')
t.expect(Array.isArray(json)).toBe(true)
t.expect(json.length).toBe(1)
t.expect(json[0].service_slug).toEqual('demo')
})
})
})
Expand Down Expand Up @@ -121,8 +121,8 @@ test('netlify addons:config demo', async (t) => {
['addons:config', 'demo', '--TWILIO_ACCOUNT_SID', 'bar'],
getCLIOptions({ builder, apiUrl }),
)
t.true(cliResponse.includes('Updating demo add-on config values'))
t.true(cliResponse.includes('Add-on "demo" successfully updated'))
t.expect(cliResponse.includes('Updating demo add-on config values')).toBe(true)
t.expect(cliResponse.includes('Add-on "demo" successfully updated')).toBe(true)
})
})
})
Expand Down Expand Up @@ -151,7 +151,7 @@ test('netlify addon:delete demo', async (t) => {

await withMockApi(deleteRoutes, async ({ apiUrl }) => {
const cliResponse = await callCli(['addons:delete', 'demo', '-f'], getCLIOptions({ builder, apiUrl }))
t.true(cliResponse.includes('Addon "demo" deleted'))
t.expect(cliResponse.includes('Addon "demo" deleted')).toBe(true)
})
})
})
Loading