Skip to content

Commit

Permalink
Achieve 100% test coverage (#370)
Browse files Browse the repository at this point in the history
  • Loading branch information
jean-michelet authored Apr 28, 2024
1 parent e107d5b commit 7e83838
Show file tree
Hide file tree
Showing 15 changed files with 252 additions and 21 deletions.
2 changes: 0 additions & 2 deletions .taprc
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
ts: true
jsx: false
flow: false
coverage: true
check-coverage: false
24 changes: 11 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ const { pathToFileURL } = require('node:url')

const isFastifyAutoloadTypescriptOverride = !!process.env.FASTIFY_AUTOLOAD_TYPESCRIPT
const isTsNode = (Symbol.for('ts-node.register.instance') in process) || !!process.env.TS_NODE_DEV
const isBabelNode = (process?.execArgv || []).concat(process?.argv || []).some((arg) => arg.indexOf('babel-node') >= 0)
const isBabelNode = process.execArgv.concat(process.argv).some((arg) => arg.indexOf('babel-node') >= 0)

const isVitestEnvironment = process.env.VITEST === 'true' || process.env.VITEST_WORKER_ID !== undefined
const isJestEnvironment = process.env.JEST_WORKER_ID !== undefined
const isSWCRegister = process._preload_modules && process._preload_modules.includes('@swc/register')
const isSWCNodeRegister = process._preload_modules && process._preload_modules.includes('@swc-node/register')
const isSWCRegister = process._preload_modules?.includes('@swc/register')
const isSWCNodeRegister = process._preload_modules?.includes('@swc-node/register')
/* istanbul ignore next - OS specific */
const isSWCNode = typeof process.env._ === 'string' && process.env._.includes('.bin/swc-node')
const isTsm = process._preload_modules && process._preload_modules.includes('tsm')
const isEsbuildRegister = process._preload_modules && process._preload_modules.includes('esbuild-register')
const isTsx = process._preload_modules && process._preload_modules.toString().includes('tsx')
const isTsm = process._preload_modules?.includes('tsm')
const isEsbuildRegister = process._preload_modules?.includes('esbuild-register')
const isTsx = process._preload_modules?.toString()?.includes('tsx')
const typescriptSupport = isFastifyAutoloadTypescriptOverride || isTsNode || isVitestEnvironment || isBabelNode || isJestEnvironment || isSWCRegister || isSWCNodeRegister || isSWCNode || isTsm || isTsx || isEsbuildRegister

const forceESMEnvironment = isVitestEnvironment || false
Expand Down Expand Up @@ -72,12 +73,9 @@ const fastifyAutoload = async function autoload (fastify, options) {
}

await Promise.all(hookArray.map((h) => {
if (hooksMeta[h.file]) return null // hook plugin already loaded, skip this instance
return loadHook(h, opts)
.then((hookPlugin) => {
if (hookPlugin) {
hooksMeta[h.file] = hookPlugin
}
hooksMeta[h.file] = hookPlugin
})
.catch((err) => {
throw enrichError(err)
Expand All @@ -96,7 +94,7 @@ const fastifyAutoload = async function autoload (fastify, options) {
for (const hookFile of hookFiles) {
const hookPlugin = hooksMeta[hookFile.file]
// encapsulate hooks at plugin level
if (hookPlugin) app.register(hookPlugin)
app.register(hookPlugin)
}
registerAllPlugins(app, pluginFiles)
}
Expand All @@ -118,7 +116,7 @@ async function getPackageType (cwd) {
const directories = cwd.split(sep)

// required for paths that begin with the sep, such as linux root
directories[0] = directories[0] !== '' ? directories[0] : sep
directories[0] = /* istanbul ignore next - OS specific */ directories[0] !== '' ? directories[0] : sep

while (directories.length > 0) {
const filePath = join(...directories, 'package.json')
Expand Down Expand Up @@ -409,7 +407,6 @@ function wrapRoutes (content) {
}

async function loadHook (hook, options) {
if (!hook) return null
let hookContent
if (options.forceESM || hook.type === 'module' || forceESMEnvironment) {
hookContent = await import(pathToFileURL(hook.file).href)
Expand All @@ -436,6 +433,7 @@ function enrichError (err) {
if (err instanceof SyntaxError) {
err.message += ' at ' + err.stack.split('\n', 1)[0]
}

return err
}

Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"typescript:vitest": "vitest run",
"typescript:vitest:dev": "vitest",
"unit": "node scripts/unit.js",
"unit:with-modules": "tap test/commonjs/*.js test/module/*.js test/typescript/*.ts"
"unit:with-modules": "tap test/issues/*/test.js test/commonjs/*.js test/module/*.js test/typescript/*.ts"
},
"repository": {
"type": "git",
Expand All @@ -43,6 +43,7 @@
"url": "https://github.com/fastify/fastify-autoload/issues"
},
"homepage": "https://github.com/fastify/fastify-autoload#readme",
"dependencies": {},
"devDependencies": {
"@fastify/pre-commit": "^2.0.2",
"@fastify/url-data": "^5.0.0",
Expand All @@ -52,6 +53,8 @@
"@types/jest": "^29.0.0",
"@types/node": "^20.1.0",
"@types/tap": "^15.0.5",
"esbuild": "^0.19.2",
"esbuild-register": "^3.4.1",
"fastify": "^4.0.0-rc.2",
"fastify-plugin": "^4.0.0",
"jest": "^28.1.3",
Expand All @@ -65,12 +68,9 @@
"tsm": "^2.2.1",
"tsx": "^3.7.1",
"typescript": "^5.0.2",
"esbuild": "^0.19.2",
"esbuild-register": "^3.4.1",
"vite": "^5.0.0",
"vitest": "^0.34.1"
},
"dependencies": {},
"standard": {
"ignore": [
"test/*/*-error/lib/a.js"
Expand Down
5 changes: 4 additions & 1 deletion scripts/unit-typescript-tsx.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

const { exec } = require('node:child_process')

const version = Number(process.version.split('.')[0].slice(1))

const args = [
'npx',
'tsx',
version >= 18 ? '--node-options=--import=tsx' : '',
'tsnd',
'test/typescript/basic.ts'
]

Expand Down
1 change: 1 addition & 0 deletions test/issues/369/invalid-autohooks/.autohooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@
Empty file.
1 change: 1 addition & 0 deletions test/issues/369/non-SyntaxError/.autohooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x
8 changes: 8 additions & 0 deletions test/issues/369/routes/.autohooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

module.exports = async function (app, opts) {
app.addHook('onRequest', async (req, reply) => {
req.hooked = req.hooked || []
req.hooked.push('root')
})
}
8 changes: 8 additions & 0 deletions test/issues/369/routes/child/.autohooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

module.exports = async function (app, opts) {
app.addHook('onRequest', async (req, reply) => {
req.hooked = req.hooked || []
req.hooked.push('child')
})
}
7 changes: 7 additions & 0 deletions test/issues/369/routes/child/routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict'

module.exports = async function (app, opts, next) {
app.get('/', async function (req, reply) {
reply.status(200).send({ hooked: req.hooked })
})
}
10 changes: 10 additions & 0 deletions test/issues/369/routes/promisified/.autohooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict'

module.exports = new Promise((res) => {
res(async function (app, opts) {
app.addHook('onRequest', async (req, reply) => {
req.hooked = req.hooked || []
req.hooked.push('promisified')
})
})
})
7 changes: 7 additions & 0 deletions test/issues/369/routes/promisified/routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict'

module.exports = async function (app, opts, next) {
app.get('/', async function (req, reply) {
reply.status(200).send({ hooked: req.hooked })
})
}
7 changes: 7 additions & 0 deletions test/issues/369/routes/routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict'

module.exports = async function (app, opts, next) {
app.get('/', async function (req, reply) {
reply.status(200).send({ hooked: req.hooked })
})
}
183 changes: 183 additions & 0 deletions test/issues/369/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
'use strict'

const { test } = require('tap')
const Fastify = require('fastify')
const path = require('path')
const autoload = require('../../..')

test('Should throw an error when trying to load invalid hooks', async (t) => {
const app = Fastify()
app.register(autoload, {
dir: path.join(__dirname, 'invalid-autohooks'),
autoHooks: true
})

await t.rejects(app.ready(), /Invalid or unexpected token/)
})

test('Should throw an error when trying to import hooks plugin using index.ts if typescriptSupport is not enabled', async (t) => {
const app = Fastify()
app.register(autoload, {
dir: path.join(__dirname, 'invalid-index-type'),
autoHooks: true
})

await t.rejects(app.ready(), new Error(`@fastify/autoload cannot import hooks plugin at '${path.join(__dirname, 'invalid-index-type/index.ts')}'`))
})

test('Should not accumulate plugin if doesn\'t comply to matchFilter', async (t) => {
const app = Fastify()
app.register(autoload, {
dir: path.join(__dirname, 'routes')
})

await app.ready()

const res = await app.inject({
url: '/'
})

t.equal(res.statusCode, 200)

const app2 = Fastify()
app2.register(autoload, {
dir: path.join(__dirname, 'routes'),
matchFilter: /invalid/
})

await app2.ready()

const res2 = await app2.inject({
url: '/'
})

t.equal(res2.statusCode, 404)
})

test('Should be able to filter paths using a string', async (t) => {
const app = Fastify()
app.register(autoload, {
dir: path.join(__dirname, 'routes'),
matchFilter: 'routes.js'
})

await app.ready()

const res = await app.inject({
url: '/'
})

t.equal(res.statusCode, 200)

const app2 = Fastify()
app2.register(autoload, {
dir: path.join(__dirname, 'routes'),
matchFilter: 'invalid-path'
})

await app2.ready()

const res2 = await app2.inject({
url: '/'
})

t.equal(res2.statusCode, 404)
})

test('Should be able to filter paths using a function', async (t) => {
const app = Fastify()
app.register(autoload, {
dir: path.join(__dirname, 'routes'),
matchFilter: (path) => path.includes('routes.js')
})

await app.ready()

const res = await app.inject({
url: '/'
})

t.equal(res.statusCode, 200)

const app2 = Fastify()
app2.register(autoload, {
dir: path.join(__dirname, 'routes'),
matchFilter: (path) => path.includes('invalid-path')
})

await app2.ready()

const res2 = await app2.inject({
url: '/'
})

t.equal(res2.statusCode, 404)
})

test('Should not accumulate plugin if ignoreFilter is matched', async (t) => {
const app = Fastify()
app.register(autoload, {
dir: path.join(__dirname, 'routes'),
ignoreFilter: /\/not-exists.js/
})

await app.ready()

const res = await app.inject({
url: '/'
})

t.equal(res.statusCode, 200)

const app2 = Fastify()
app2.register(autoload, {
dir: path.join(__dirname, 'routes'),
ignoreFilter: /\/routes.js/,
autoHooks: true
})

await app2.ready()

const res2 = await app2.inject({
url: '/'
})

t.equal(res2.statusCode, 404)
})

test('Should not set skip-override if hook plugin is not a function or async function', async (t) => {
const app = Fastify()
app.register(autoload, {
dir: path.join(__dirname, 'routes'),
autoHooks: true,
cascadeHooks: true
})

app.decorateRequest('hooked', '')

await app.ready()

const res = await app.inject({
url: '/child'
})

t.equal(res.statusCode, 200)
t.same(JSON.parse(res.payload), { hooked: ['root', 'child'] })

const res2 = await app.inject({
url: '/promisified'
})

t.equal(res2.statusCode, 200)
t.same(JSON.parse(res2.payload), { hooked: ['root'] })
})

test('Should not enrich non-SyntaxError', async (t) => {
const app = Fastify()
app.register(autoload, {
dir: path.join(__dirname, 'non-SyntaxError'),
autoHooks: true
})

t.rejects(app.ready(), new ReferenceError('x is not defined'))
})
2 changes: 1 addition & 1 deletion test/typescript-esm/forceESM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ app.ready(function (err): void {
{
url: '/installed'
},
function (err, res): void {
function (err, res: any): void {
t.error(err)
t.equal(res.statusCode, 200)
t.same(JSON.parse(res.payload), { result: 'ok' })
Expand Down

0 comments on commit 7e83838

Please sign in to comment.