Files
shields/core/server/server.spec.js
Paul Melnikow d8ce045ead Adopt Gatsby (#2906)
While Next.js can handle static sites, we've had a few issues with it, notably a performance hit at runtime and some bugginess around routing and SSR. Gatsby being fully intended for high-performance static sites makes it a great technical fit for the Shields frontend. The `createPages()` API should be a really nice way to add a page for each service family, for example.

This migrates the frontend from Next.js to Gatsby. Gatsby is a powerful tool, which has a bit of downside as there's a lot to dig through. Overall I found configuration easier than Next.js. There are a lot of plugins and for the most part they worked out of the box. The documentation is good.

Links are cleaner now: there is no #. This will break old links though perhaps we could add some redirection to help with that. The only one I’m really concerned about `/#/endpoint`. I’m not sure if folks are deep-linking to the category pages.

There are a lot of enhancements we could add, in order to speed up the site even more. In particular we could think about inlining the SVGs rather than making separate requests for each one.

While Gatsby recommends GraphQL, it's not required. To keep things simple and reduce the learning curve, I did not use it here.

Close #1943 
Fix #2837 Fix #2616
2019-02-06 16:37:55 -05:00

159 lines
4.8 KiB
JavaScript

'use strict'
const { expect } = require('chai')
const fetch = require('node-fetch')
const got = require('got')
const fs = require('fs')
const isPng = require('is-png')
const isSvg = require('is-svg')
const path = require('path')
const sinon = require('sinon')
const portfinder = require('portfinder')
const Joi = require('joi')
const svg2img = require('../../gh-badges/lib/svg-to-img')
const { createTestServer } = require('./in-process-server-test-helpers')
describe('The server', function() {
let server, baseUrl
before('Start the server', async function() {
// Fixes https://github.com/badges/shields/issues/2611
this.timeout(10000)
const port = await portfinder.getPortPromise()
server = createTestServer({ port })
baseUrl = server.baseUrl
await server.start()
})
after('Shut down the server', async function() {
if (server) {
await server.stop()
}
server = undefined
})
it('should produce colorscheme badges', async function() {
const res = await fetch(`${baseUrl}:fruit-apple-green.svg`)
expect(res.ok).to.be.true
expect(await res.text())
.to.satisfy(isSvg)
.and.to.include('fruit')
.and.to.include('apple')
})
it('should produce colorscheme PNG badges', async function() {
const res = await fetch(`${baseUrl}:fruit-apple-green.png`)
expect(res.ok).to.be.true
expect(await res.buffer()).to.satisfy(isPng)
})
it('should preserve label case', async function() {
const res = await fetch(`${baseUrl}:fRuiT-apple-green.svg`)
expect(res.ok).to.be.true
expect(await res.text())
.to.satisfy(isSvg)
.and.to.include('fRuiT')
})
// https://github.com/badges/shields/pull/1319
it('should not crash with a numeric logo', async function() {
const res = await fetch(`${baseUrl}:fruit-apple-green.svg?logo=1`)
expect(res.ok).to.be.true
expect(await res.text())
.to.satisfy(isSvg)
.and.to.include('fruit')
.and.to.include('apple')
})
it('should not crash with a numeric link', async function() {
const res = await fetch(`${baseUrl}:fruit-apple-green.svg?link=1`)
expect(res.ok).to.be.true
expect(await res.text())
.to.satisfy(isSvg)
.and.to.include('fruit')
.and.to.include('apple')
})
it('should not crash with a boolean link', async function() {
const res = await fetch(`${baseUrl}:fruit-apple-green.svg?link=true`)
expect(res.ok).to.be.true
expect(await res.text())
.to.satisfy(isSvg)
.and.to.include('fruit')
.and.to.include('apple')
})
it('should return the 404 badge for unknown badges', async function() {
const res = await fetch(`${baseUrl}this/is/not/a/badge.svg`)
expect(res.status).to.equal(404)
expect(await res.text())
.to.satisfy(isSvg)
.and.to.include('404')
.and.to.include('badge not found')
})
it('should return the 404 html page for rando links', async function() {
const res = await fetch(`${baseUrl}this/is/most/definitely/not/a/badge.js`)
expect(res.status).to.equal(404)
expect(await res.text()).to.include('blood, toil, tears and sweat')
})
it('should redirect the root as configured', async function() {
const res = await got(baseUrl, { followRedirect: false })
expect(res.statusCode).to.equal(302)
// This value is set in `config/test.yml`
expect(res.headers.location).to.equal('http://badge-server.example.com')
})
context('with svg2img error', function() {
const expectedError = fs.readFileSync(
path.resolve(__dirname, 'error-pages', '500.html')
)
let toBufferStub
beforeEach(function() {
toBufferStub = sinon
.stub(svg2img._imageMagick.prototype, 'toBuffer')
.callsArgWith(1, Error('whoops'))
})
afterEach(function() {
toBufferStub.restore()
})
it('should emit the 500 message', async function() {
const res = await fetch(`${baseUrl}:some_new-badge-green.png`)
// This emits status code 200, though 500 would be preferable.
expect(res.status).to.equal(200)
expect(await res.text()).to.include(expectedError)
})
})
describe('analytics endpoint', function() {
it('should return analytics in the expected format', async function() {
const countSchema = Joi.array()
.items(
Joi.number()
.integer()
.min(0)
.required()
)
.length(36)
.required()
const analyticsSchema = Joi.object({
vendorMonthly: countSchema,
rawMonthly: countSchema,
vendorFlatMonthly: countSchema,
rawFlatMonthly: countSchema,
vendorFlatSquareMonthly: countSchema,
rawFlatSquareMonthly: countSchema,
}).required()
const res = await fetch(`${baseUrl}$analytics/v1`)
expect(res.ok).to.be.true
const json = await res.json()
Joi.assert(json, analyticsSchema)
})
})
})