webpack-bugsnag-plugins
Version:
Webpack plugins for common Bugsnag actions
319 lines (296 loc) • 10.4 kB
JavaScript
import test from 'tape'
import Plugin from '../source-map-uploader-plugin.js'
import { createServer } from 'http'
import formidable from 'formidable'
import { exec } from 'child_process'
import { join, dirname } from 'path'
import { fileURLToPath } from 'url'
import once from 'once'
import fs from 'fs'
const __dirname = dirname(fileURLToPath(import.meta.url))
const generateEnv = (server, other = {}) => {
// The openssl-legacy-provider is required for webpack4 in node 18 and up - see https://github.com/webpack/webpack/issues/14532
const nodeOptions = (parseInt(process.versions.node.split('.')[0]) >= 18) ? { NODE_OPTIONS: '--openssl-legacy-provider' } : {}
return Object.assign({}, process.env, { PORT: server.address().port }, nodeOptions, other)
}
const validateParts = (parts, t, end) => {
const data = fs.readFileSync(parts.sourceMap[0].filepath)
t.equal(parts.sourceMap[0].mimetype, 'application/octet-stream')
try {
t.ok(JSON.parse(data), 'sourceMap should be valid json')
} catch (e) {
end(e)
}
t.equal(parts.minifiedFile[0].mimetype, 'application/octet-stream')
t.ok(fs.readFileSync(parts.sourceMap[0].filepath).length !== 0, 'js bundle should have size')
}
test('BugsnagSourceMapUploaderPlugin', t => {
try {
const p = new Plugin({})
p.apply()
} catch (e) {
t.ok(e, 'api key should be required')
t.ok(/apiKey/.test(e.message), 'apiKey should be present in error message')
}
try {
t.ok(new Plugin({ apiKey: 'YOUR_API_KEY' }), 'it should work with all required options')
} catch (e) {
t.fail(e.message)
}
t.end()
})
test('it sends upon successful build (example project #1)', t => {
const end = err => {
server.close()
if (err) return t.fail(err.message)
t.end()
}
t.plan(8)
const server = createServer((req, res) => {
formidable().parse(req, once(function (err, fields, parts) {
if (err) {
res.end('ERR')
return end(err)
}
t.equal(fields.apiKey[0], 'YOUR_API_KEY', 'body should contain api key')
t.equal(fields.codeBundleId[0], '1.0.0-b12', 'body could contain codeBundleId')
t.ok(/^https:\/\/foobar.com\/js\/main\.js\?[\w\d]+$/.test(fields.minifiedUrl[0]), 'body should contain minified url')
t.equal(Object.keys(parts).length, 2, 'body should contain 2 uploads')
validateParts(parts, t, end)
res.end('OK')
end()
}))
})
server.listen()
exec(join(__dirname, '..', 'node_modules', '.bin', 'webpack'), {
env: generateEnv(server),
cwd: join(__dirname, 'fixtures', 'd')
}, (err, stdout, stderr) => {
if (err) {
console.info(err, '\n\n\n', stdout, '\n\n\n', stderr)
end(err)
}
})
})
test('it sends upon successful build (example project #2)', t => {
const end = err => {
server.close()
if (err) return t.fail(err.message)
t.end()
}
t.plan(7)
const server = createServer((req, res) => {
formidable().parse(req, once(function (err, fields, parts) {
if (err) {
res.end('ERR')
return end(err)
}
t.equal(fields.apiKey[0], 'YOUR_API_KEY', 'body should contain api key')
t.equal(fields.minifiedUrl[0], 'https://foobar.com/js/bundle.js', 'body should contain minified url')
t.equal(Object.keys(parts).length, 2, 'body should contain 2 uploads')
validateParts(parts, t, end)
res.end('OK')
end()
}))
})
server.listen()
exec(join(__dirname, '..', 'node_modules', '.bin', 'webpack'), {
env: generateEnv(server),
cwd: join(__dirname, 'fixtures', 'c')
}, (err, stdout, stderr) => {
if (err) {
console.info(err, '\n\n\n', stdout, '\n\n\n', stderr)
end(err)
}
})
})
if (process.env.WEBPACK_VERSION !== '3') {
test('it’s able to locate the files when source maps are written to a different directory', t => {
const end = err => {
server.close()
if (err) return t.fail(err.message)
t.end()
}
t.plan(7)
const server = createServer((req, res) => {
formidable().parse(req, once(function (err, fields, parts) {
if (err) {
res.end('ERR')
return end(err)
}
t.equal(fields.apiKey[0], 'YOUR_API_KEY', 'body should contain api key')
t.equal(fields.minifiedUrl[0], '*/dist/main.js', 'body should contain minified url')
t.equal(Object.keys(parts).length, 2, 'body should contain 2 uploads')
validateParts(parts, t, end)
res.end('OK')
end()
}))
})
server.listen()
exec(join(__dirname, '..', 'node_modules', '.bin', 'webpack'), {
env: generateEnv(server),
cwd: join(__dirname, 'fixtures', 'f')
}, (err, stdout, stderr) => {
if (err) {
console.info(err, '\n\n\n', stdout, '\n\n\n', stderr)
end(err)
}
})
test('it removes any "../" from chunk paths', t => {
const end = err => {
server.close()
if (err) return t.fail(err.message)
t.end()
}
t.plan(7)
const server = createServer((req, res) => {
formidable().parse(req, once(function (err, fields, parts) {
if (err) {
res.end('ERR')
return end(err)
}
t.equal(fields.apiKey[0], 'YOUR_API_KEY', 'body should contain api key')
t.equal(fields.minifiedUrl[0], 'https://foobar.com/js/static/chunks/main.js', 'body should contain minified url')
t.equal(Object.keys(parts).length, 2, 'body should contain 2 uploads')
validateParts(parts, t, end)
res.end('OK')
end()
}))
})
server.listen()
exec(join(__dirname, '..', 'node_modules', '.bin', 'webpack'), {
env: generateEnv(server),
cwd: join(__dirname, 'fixtures', 'h')
}, (err, stdout, stderr) => {
if (err) {
console.info(err, '\n\n\n', stdout, '\n\n\n', stderr)
end(err)
}
})
})
test('it ignores source maps for css files by default', t => {
t.plan(7)
t.plan(3)
const requests = []
const end = err => {
clearTimeout(timeout)
server.close()
if (err) return t.fail(err.message)
t.end()
}
// prevent test hanging forever
const timeout = setTimeout(end, 10000)
const done = () => {
t.equal(requests[0].minifiedUrl, '*/dist/main.js')
t.match(requests[0].parts.find(p => p.filename.includes('.js') && !p.filename.includes('.map'))?.filename, /main\.js$/)
t.match(requests[0].parts.find(p => p.filename.includes('.js.map'))?.filename, /main\.js\.map$/)
end()
}
const server = createServer((req, res) => {
formidable().parse(req, once(function (err, fields, parts) {
if (err) {
res.end('ERR')
return end(err)
}
requests.push({
apiKey: fields.apiKey[0],
minifiedUrl: fields.minifiedUrl[0],
parts: Object.keys(parts).map(name => ({ name, filename: parts[name][0].originalFilename }))
})
res.end('OK')
done()
}))
})
server.listen()
exec(join(__dirname, '..', 'node_modules', '.bin', 'webpack'), {
env: generateEnv(server),
cwd: join(__dirname, 'fixtures', 'e')
}, (err, stdout, stderr) => {
if (err) {
console.info(err, '\n\n\n', stdout, '\n\n\n', stderr)
end(err)
}
})
})
})
test('it uploads css map files if you really want', t => {
t.plan(6)
const requests = []
const end = err => {
clearTimeout(timeout)
server.close()
if (err) return t.fail(err.message)
t.end()
}
// prevent test hanging forever
const timeout = setTimeout(end, 10000)
const done = () => {
requests.sort((a, b) => a.minifiedUrl < b.minifiedUrl ? -1 : 1)
t.equal(requests[0].minifiedUrl, '*/dist/main.css')
t.match(requests[0].parts.find(p => p.filename.includes('.css'))?.filename, /main\.css/)
t.match(requests[0].parts.find(p => p.filename.includes('.map'))?.filename, /main\.css\.map/)
t.equal(requests[1].minifiedUrl, '*/dist/main.js')
t.match(requests[1].parts.find(p => p.filename.includes('.js') && !p.filename.includes('.map'))?.filename, /main\.js/)
t.match(requests[1].parts.find(p => p.filename.includes('.js.map'))?.filename, /main\.js\.map/)
end()
}
const server = createServer((req, res) => {
formidable().parse(req, once(function (err, fields, parts) {
if (err) {
res.end('ERR')
return end(err)
}
requests.push({
apiKey: fields.apiKey[0],
minifiedUrl: fields.minifiedUrl[0],
parts: Object.keys(parts).map(name => ({ name, filename: parts[name][0].originalFilename }))
})
res.end('OK')
if (requests.length < 2) return
done()
}))
})
server.listen()
exec(join(__dirname, '..', 'node_modules', '.bin', 'webpack'), {
env: generateEnv(server, { IGNORED_EXTENSIONS: '.php,.exe' }),
cwd: join(__dirname, 'fixtures', 'e')
}, (err, stdout, stderr) => {
if (err) {
console.info(err, '\n\n\n', stdout, '\n\n\n', stderr)
end(err)
}
})
})
test('it sends upon successful build (output.futureEmitAssets=true)', t => {
const end = err => {
server.close()
if (err) return t.fail(err.message)
t.end()
}
t.plan(7)
const server = createServer((req, res) => {
formidable().parse(req, once(function (err, fields, parts) {
if (err) {
res.end('ERR')
return end(err)
}
t.equal(fields.apiKey[0], 'YOUR_API_KEY', 'body should contain api key')
t.equal(fields.minifiedUrl[0], 'https://foobar.com/js/bundle.js', 'body should contain minified url')
t.equal(Object.keys(parts).length, 2, 'body should contain 2 uploads')
validateParts(parts, t, end)
res.end('OK')
end()
}))
})
server.listen()
exec(join(__dirname, '..', 'node_modules', '.bin', 'webpack'), {
env: generateEnv(server),
cwd: join(__dirname, 'fixtures', 'g')
}, (err, stdout, stderr) => {
if (err) {
console.info(err, '\n\n\n', stdout, '\n\n\n', stderr)
end(err)
}
})
})
}