mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 06:26:26 +00:00
77dddd5e8a
Release branch for 4.0.0.
172 lines
6.1 KiB
TypeScript
172 lines
6.1 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { COUNTRY_SHAPES } from '../../../src/components/Studio/countryShapes'
|
|
import {
|
|
FLAG_H, FLAG_SPECS, FLAG_W, flagBands, flagDisc, flagSpec,
|
|
} from '../../../src/components/Studio/flags'
|
|
|
|
/**
|
|
* The drawn flags (#1973).
|
|
*
|
|
* The rule that matters is the one about restraint: a flag is listed only when
|
|
* its construction is exact. A wrong flag in a printed book is worse than a
|
|
* country outline, so the guard here is as much about what is *absent* as
|
|
* about what is drawn.
|
|
*/
|
|
|
|
describe('the flag table', () => {
|
|
it('uses two-letter uppercase codes throughout', () => {
|
|
for (const code of Object.keys(FLAG_SPECS)) {
|
|
expect(code, code).toMatch(/^[A-Z]{2}$/)
|
|
}
|
|
})
|
|
|
|
it('gives every colour as #rrggbb, so it can be handed straight to SVG', () => {
|
|
for (const [code, spec] of Object.entries(FLAG_SPECS)) {
|
|
const colours = spec.t === 'h' || spec.t === 'v'
|
|
? spec.c
|
|
: spec.t === 'nordic'
|
|
? [spec.field, spec.cross, ...(spec.inner ? [spec.inner] : [])]
|
|
: spec.t === 'cross'
|
|
? [spec.field, spec.cross]
|
|
: [spec.field, spec.disc]
|
|
for (const c of colours) expect(c, `${code}: ${c}`).toMatch(/^#[0-9a-f]{6}$/)
|
|
}
|
|
})
|
|
|
|
it('has at least two bands on any striped flag', () => {
|
|
for (const [code, spec] of Object.entries(FLAG_SPECS)) {
|
|
if (spec.t === 'h' || spec.t === 'v') {
|
|
expect(spec.c.length, code).toBeGreaterThanOrEqual(2)
|
|
}
|
|
}
|
|
})
|
|
|
|
/*
|
|
* The point of the whole approach: anything that cannot be built from bands,
|
|
* a cross or a disc is left out rather than approximated. These are the flags
|
|
* most likely to be reached for and wrongly added — each carries an emblem or
|
|
* unequal bands that this construction cannot express.
|
|
*/
|
|
it('leaves out the flags it cannot draw exactly', () => {
|
|
for (const code of ['GB', 'US', 'ES', 'PT', 'MX', 'BR', 'IN', 'EG', 'CA', 'AU', 'CO', 'LV', 'GR', 'TR']) {
|
|
expect(FLAG_SPECS[code], code).toBeUndefined()
|
|
}
|
|
})
|
|
|
|
it('has a country silhouette for every flag it does not draw', () => {
|
|
// The fallback has to exist, or a missing flag becomes bare letters.
|
|
for (const code of ['GB', 'US', 'ES', 'PT', 'MX', 'BR', 'IN', 'CA', 'AU', 'TR', 'GR']) {
|
|
expect(COUNTRY_SHAPES[code], code).toBeTruthy()
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('flagBands', () => {
|
|
it('splits horizontal bands evenly across the height', () => {
|
|
const bands = flagBands({ t: 'h', c: ['#000000', '#dd0000', '#ffce00'] })
|
|
expect(bands).toHaveLength(3)
|
|
for (const b of bands) {
|
|
expect(b.w).toBe(FLAG_W)
|
|
expect(b.h).toBeCloseTo(FLAG_H / 3, 6)
|
|
}
|
|
expect(bands[0].y).toBe(0)
|
|
expect(bands[2].y + bands[2].h).toBeCloseTo(FLAG_H, 6)
|
|
})
|
|
|
|
it('splits vertical bands evenly across the width', () => {
|
|
const bands = flagBands({ t: 'v', c: ['#002395', '#ffffff', '#ed2939'] })
|
|
for (const b of bands) {
|
|
expect(b.h).toBe(FLAG_H)
|
|
expect(b.w).toBeCloseTo(FLAG_W / 3, 6)
|
|
}
|
|
expect(bands[2].x + bands[2].w).toBeCloseTo(FLAG_W, 6)
|
|
})
|
|
|
|
it('keeps the band order, which is the whole identity of a tricolour', () => {
|
|
const bands = flagBands(FLAG_SPECS.DE)
|
|
expect(bands.map(b => b.fill)).toEqual(['#000000', '#dd0000', '#ffce00'])
|
|
})
|
|
|
|
/*
|
|
* Left of centre is what separates a Nordic cross from a plain one — the
|
|
* upright at the halfway mark would be the Swiss flag's geometry, not
|
|
* Denmark's.
|
|
*/
|
|
it('puts the Nordic upright left of centre', () => {
|
|
const bands = flagBands(FLAG_SPECS.DK)
|
|
const upright = bands[1]
|
|
expect(upright.x + upright.w / 2).toBeLessThan(FLAG_W / 2)
|
|
expect(upright.h).toBe(FLAG_H)
|
|
})
|
|
|
|
it('draws the inner cross on top of the outer one, and thinner', () => {
|
|
const bands = flagBands(FLAG_SPECS.IS)
|
|
// field, upright, crossbar, then the two inner bars.
|
|
expect(bands).toHaveLength(5)
|
|
expect(bands[3].fill).toBe('#dc1e35')
|
|
expect(bands[3].w).toBeLessThan(bands[1].w)
|
|
})
|
|
|
|
it('omits the inner cross where the flag has none', () => {
|
|
expect(flagBands(FLAG_SPECS.DK)).toHaveLength(3)
|
|
})
|
|
|
|
it('centres the Swiss cross both ways and keeps it inside the field', () => {
|
|
const [field, upright, bar] = flagBands(FLAG_SPECS.CH)
|
|
expect(field.w).toBe(FLAG_W)
|
|
expect(upright.x + upright.w / 2).toBeCloseTo(FLAG_W / 2, 6)
|
|
expect(bar.y + bar.h / 2).toBeCloseTo(FLAG_H / 2, 6)
|
|
expect(upright.y).toBeGreaterThan(0)
|
|
expect(upright.y + upright.h).toBeLessThan(FLAG_H)
|
|
})
|
|
|
|
it('keeps every band inside the flag box', () => {
|
|
for (const [code, spec] of Object.entries(FLAG_SPECS)) {
|
|
for (const b of flagBands(spec)) {
|
|
expect(b.x, code).toBeGreaterThanOrEqual(0)
|
|
expect(b.y, code).toBeGreaterThanOrEqual(0)
|
|
expect(b.x + b.w, code).toBeLessThanOrEqual(FLAG_W + 0.001)
|
|
expect(b.y + b.h, code).toBeLessThanOrEqual(FLAG_H + 0.001)
|
|
}
|
|
}
|
|
})
|
|
|
|
it('covers the whole box with its first band, so no page shows through', () => {
|
|
for (const [code, spec] of Object.entries(FLAG_SPECS)) {
|
|
const bands = flagBands(spec)
|
|
const area = bands.reduce((sum, b) => sum + b.w * b.h, 0)
|
|
expect(area, code).toBeGreaterThanOrEqual(FLAG_W * FLAG_H - 0.001)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('flagDisc', () => {
|
|
it('is a centred circle on a disc flag', () => {
|
|
const disc = flagDisc(FLAG_SPECS.JP)!
|
|
expect(disc.cx).toBe(FLAG_W / 2)
|
|
expect(disc.cy).toBe(FLAG_H / 2)
|
|
expect(disc.r).toBeLessThan(FLAG_H / 2)
|
|
expect(disc.fill).toBe('#bc002d')
|
|
})
|
|
|
|
it('is null for every other construction', () => {
|
|
expect(flagDisc(FLAG_SPECS.DE)).toBeNull()
|
|
expect(flagDisc(FLAG_SPECS.IS)).toBeNull()
|
|
expect(flagDisc(FLAG_SPECS.CH)).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('flagSpec', () => {
|
|
it('is case-insensitive, because codes arrive from several places', () => {
|
|
expect(flagSpec('is')).toBe(FLAG_SPECS.IS)
|
|
expect(flagSpec('IS')).toBe(FLAG_SPECS.IS)
|
|
})
|
|
|
|
it('is null for nothing and for a country with no construction', () => {
|
|
expect(flagSpec(null)).toBeNull()
|
|
expect(flagSpec(undefined)).toBeNull()
|
|
expect(flagSpec('')).toBeNull()
|
|
expect(flagSpec('GB')).toBeNull()
|
|
})
|
|
})
|