Refactor _makeBadgeData -> coalesceBadge (#2859)

`base.js` is pretty long and `_makeBadgeData` is one of the most complex
functions in it.

It's a pure function so it's easy to test in isolation. This moves the function into its own module and reorganizes the tests in a way that makes it evaluate what it's doing, and easier to test what is and isn't covered.

Ref https://github.com/badges/shields/pull/2796#discussion_r249251008
This commit is contained in:
Paul Melnikow
2019-01-24 22:48:45 -05:00
committed by GitHub
parent 2db22abd11
commit 23fe3927b2
7 changed files with 438 additions and 388 deletions

View File

@@ -4,6 +4,7 @@ const makeBadge = require('../../gh-badges/lib/make-badge')
const BaseService = require('./base')
const { setCacheHeaders } = require('./cache-headers')
const { makeSend } = require('./legacy-result-sender')
const coalesceBadge = require('./coalesce-badge')
// Badges are subject to two independent types of caching: in-memory and
// downstream.
@@ -35,7 +36,13 @@ module.exports = class NonMemoryCachingBaseService extends BaseService {
queryParams
)
const badgeData = this._makeBadgeData(queryParams, serviceData)
const badgeData = coalesceBadge(
queryParams,
serviceData,
this.defaultBadgeData,
this
)
// The final capture group is the extension.
const format = match.slice(-1)[0]
badgeData.format = format

View File

@@ -8,6 +8,7 @@ const {
setCacheHeadersForStaticResource,
} = require('./cache-headers')
const { makeSend } = require('./legacy-result-sender')
const coalesceBadge = require('./coalesce-badge')
module.exports = class BaseStaticService extends BaseService {
static register({ camp }, serviceConfig) {
@@ -33,7 +34,13 @@ module.exports = class BaseStaticService extends BaseService {
queryParams
)
const badgeData = this._makeBadgeData(queryParams, serviceData)
const badgeData = coalesceBadge(
queryParams,
serviceData,
this.defaultBadgeData,
this
)
// The final capture group is the extension.
const format = match.slice(-1)[0]
badgeData.format = format

View File

@@ -5,14 +5,8 @@ const emojic = require('emojic')
const pathToRegexp = require('path-to-regexp')
const Joi = require('joi')
const { checkErrorResponse } = require('../../lib/error-helper')
const { toArray } = require('../../lib/badge-data')
const { svg2base64 } = require('../../lib/svg-helpers')
const {
decodeDataUrlFromQueryParam,
prepareNamedLogo,
} = require('../../lib/logos')
const { assertValidCategory } = require('../../services/categories')
const coalesce = require('./coalesce')
const coalesceBadge = require('./coalesce-badge')
const {
NotFound,
InvalidResponse,
@@ -348,143 +342,6 @@ class BaseService {
return serviceData
}
// Translate modern badge data to the legacy schema understood by the badge
// maker. Allow the user to override the label, color, logo, etc. through
// the query string. Provide support for most badge options via
// `serviceData` so the Endpoint badge can specify logos and colors, though
// allow that the user's logo or color to take precedence. A notable
// exception is the case of errors. When the service specifies that an error
// has occurred, the user's requested color does not override the error color.
//
// Logos are resolved in this manner:
//
// 1. When `?logo=` contains the name of one of the Shields logos, or contains
// base64-encoded SVG, that logo is used. In the case of a named logo, when
// a `&logoColor=` is specified, that color is used. Otherwise the default
// color is used. `logoColor` will not be applied to a custom
// (base64-encoded) logo; if a custom color is desired the logo should be
// recolored prior to making the request. The appearance of the logo can be
// customized using `logoWidth`, and in the case of the popout badge,
// `logoPosition`. When `?logo=` is specified, any logo-related parameters
// specified dynamically by the service, or by default in the service, are
// ignored.
// 2. The second precedence is the dynamic logo returned by a service. This is
// used only by the Endpoint badge. The `logoColor` can be overridden by the
// query string.
// 3. In the case of the `social` style only, the last precedence is the
// service's default logo. The `logoColor` can be overridden by the query
// string.
static _makeBadgeData(overrides, serviceData) {
const {
style: overrideStyle,
label: overrideLabel,
logoColor: overrideLogoColor,
link: overrideLink,
} = overrides
// Scoutcamp converts numeric query params to numbers. Convert them back.
let {
colorB: overrideColor,
colorA: overrideLabelColor,
logoWidth: overrideLogoWidth,
logoPosition: overrideLogoPosition,
} = overrides
if (typeof overrideColor === 'number') {
overrideColor = `${overrideColor}`
}
if (typeof overrideLabelColor === 'number') {
overrideLabelColor = `${overrideLabelColor}`
}
overrideLogoWidth = +overrideLogoWidth || undefined
overrideLogoPosition = +overrideLogoPosition || undefined
// `?logo=` could be a named logo or encoded svg. Split up these cases.
const overrideLogoSvgBase64 = decodeDataUrlFromQueryParam(overrides.logo)
const overrideNamedLogo = overrideLogoSvgBase64 ? undefined : overrides.logo
const {
isError,
label: serviceLabel,
message: serviceMessage,
color: serviceColor,
labelColor: serviceLabelColor,
logoSvg: serviceLogoSvg,
namedLogo: serviceNamedLogo,
logoColor: serviceLogoColor,
logoWidth: serviceLogoWidth,
logoPosition: serviceLogoPosition,
link: serviceLink,
cacheSeconds: serviceCacheSeconds,
style: serviceStyle,
} = serviceData
const serviceLogoSvgBase64 = serviceLogoSvg
? svg2base64(serviceLogoSvg)
: undefined
const {
color: defaultColor,
namedLogo: defaultNamedLogo,
label: defaultLabel,
labelColor: defaultLabelColor,
} = this.defaultBadgeData
const defaultCacheSeconds = this._cacheLength
const style = coalesce(overrideStyle, serviceStyle)
const namedLogoSvgBase64 = prepareNamedLogo({
name: coalesce(
overrideNamedLogo,
serviceNamedLogo,
style === 'social' ? defaultNamedLogo : undefined
),
color: coalesce(
overrideLogoColor,
// If the logo has been overridden it does not make sense to inherit
// the color.
overrideNamedLogo ? undefined : serviceLogoColor
),
style,
})
return {
text: [
// Use `coalesce()` to support empty labels and messages, as in the
// static badge.
coalesce(overrideLabel, serviceLabel, defaultLabel, this.category),
coalesce(serviceMessage, 'n/a'),
],
color: coalesce(
// In case of an error, disregard user's color override.
isError ? undefined : overrideColor,
serviceColor,
defaultColor,
'lightgrey'
),
labelColor: coalesce(
// In case of an error, disregard user's color override.
isError ? undefined : overrideLabelColor,
serviceLabelColor,
defaultLabelColor
),
template: style,
logo: coalesce(
overrideLogoSvgBase64,
serviceLogoSvgBase64,
namedLogoSvgBase64
),
logoWidth: coalesce(
overrideLogoWidth,
// If the logo has been overridden it does not make sense to inherit
// the width or position.
overrideNamedLogo ? undefined : serviceLogoWidth
),
logoPosition: coalesce(
overrideLogoPosition,
overrideNamedLogo ? undefined : serviceLogoPosition
),
links: toArray(overrideLink || serviceLink),
cacheLengthSeconds: coalesce(serviceCacheSeconds, defaultCacheSeconds),
}
}
static register({ camp, handleRequest, githubApiProvider }, serviceConfig) {
const { cacheHeaders: cacheHeaderConfig, fetchLimitBytes } = serviceConfig
camp.route(
@@ -504,7 +361,12 @@ class BaseService {
queryParams
)
const badgeData = this._makeBadgeData(queryParams, serviceData)
const badgeData = coalesceBadge(
queryParams,
serviceData,
this.defaultBadgeData,
this
)
// The final capture group is the extension.
const format = match.slice(-1)[0]
sendBadge(format, badgeData)

View File

@@ -4,7 +4,6 @@ const Joi = require('joi')
const { expect } = require('chai')
const { test, given, forCases } = require('sazerac')
const sinon = require('sinon')
const { getShieldsIcon } = require('../../lib/logos')
const trace = require('./trace')
const {
@@ -371,245 +370,6 @@ describe('BaseService', function() {
})
})
describe('_makeBadgeData', function() {
describe('Overrides', function() {
it('overrides the label', function() {
const badgeData = DummyService._makeBadgeData(
{ label: 'purr count' },
{ label: 'purrs' }
)
expect(badgeData.text).to.deep.equal(['purr count', 'n/a'])
})
it('overrides the label color', function() {
const badgeData = DummyService._makeBadgeData(
{ colorA: '42f483' },
{ color: 'green' }
)
expect(badgeData.labelColor).to.equal('42f483')
})
it('overrides the color', function() {
const badgeData = DummyService._makeBadgeData(
{ colorB: '10ADED' },
{ color: 'red' }
)
expect(badgeData.color).to.equal('10ADED')
})
it('converts a query-string numeric color to a string', function() {
const badgeData = DummyService._makeBadgeData(
// Scoutcamp converts numeric query params to numbers.
{ colorB: 123 },
{ color: 'green' }
)
expect(badgeData.color).to.equal('123')
})
it('does not override the color in case of an error', function() {
const badgeData = DummyService._makeBadgeData(
{ colorB: '10ADED' },
{ isError: true, color: 'lightgray' }
)
expect(badgeData.color).to.equal('lightgray')
})
it('overrides the logo', function() {
const badgeData = DummyService._makeBadgeData(
{ logo: 'github' },
{ namedLogo: 'appveyor' }
)
// .not.be.empty for confidence that nothing has changed with `getShieldsIcon()`.
expect(badgeData.logo).to.equal(getShieldsIcon({ name: 'github' })).and
.not.be.empty
})
it('overrides the logo with a color', function() {
const badgeData = DummyService._makeBadgeData(
{ logo: 'github', logoColor: 'blue' },
{ namedLogo: 'appveyor' }
)
expect(badgeData.logo).to.equal(
getShieldsIcon({ name: 'github', color: 'blue' })
).and.not.be.empty
})
it("when the logo is overridden, it ignores the service's logo color, position, and width", function() {
const badgeData = DummyService._makeBadgeData(
{ logo: 'github' },
{
namedLogo: 'appveyor',
logoColor: 'red',
logoPosition: -3,
logoWidth: 100,
}
)
expect(badgeData.logo).to.equal(getShieldsIcon({ name: 'github' })).and
.not.be.empty
})
it("overrides the service logo's color", function() {
const badgeData = DummyService._makeBadgeData(
{ logoColor: 'blue' },
{ namedLogo: 'github', logoColor: 'red' }
)
expect(badgeData.logo).to.equal(
getShieldsIcon({ name: 'github', color: 'blue' })
).and.not.be.empty
})
it('overrides the logo with custom svg', function() {
const logoSvg = 'data:image/svg+xml;base64,PHN2ZyB4bWxu'
const badgeData = DummyService._makeBadgeData(
{ logo: logoSvg },
{ namedLogo: 'appveyor' }
)
expect(badgeData.logo).to.equal(logoSvg)
})
it('ignores the color when custom svg is provided', function() {
const logoSvg = 'data:image/svg+xml;base64,PHN2ZyB4bWxu'
const badgeData = DummyService._makeBadgeData(
{ logo: logoSvg, logoColor: 'brightgreen' },
{ namedLogo: 'appveyor' }
)
expect(badgeData.logo).to.equal(logoSvg)
})
it('overrides the logoWidth', function() {
const badgeData = DummyService._makeBadgeData({ logoWidth: 20 }, {})
expect(badgeData.logoWidth).to.equal(20)
})
it('overrides the logoPosition', function() {
const badgeData = DummyService._makeBadgeData({ logoPosition: -10 }, {})
expect(badgeData.logoPosition).to.equal(-10)
})
it('overrides the links', function() {
const badgeData = DummyService._makeBadgeData(
{ link: 'https://circleci.com/gh/badges/daily-tests' },
{
link:
'https://circleci.com/workflow-run/184ef3de-4836-4805-a2e4-0ceba099f92d',
}
)
expect(badgeData.links).to.deep.equal([
'https://circleci.com/gh/badges/daily-tests',
])
})
it('overrides the template', function() {
const badgeData = DummyService._makeBadgeData({ style: 'pill' }, {})
expect(badgeData.template).to.equal('pill')
})
it('overrides the cache length', function() {
const badgeData = DummyService._makeBadgeData(
{ style: 'pill' },
{ cacheSeconds: 123 }
)
expect(badgeData.cacheLengthSeconds).to.equal(123)
})
})
describe('Service data', function() {
it('applies the service message', function() {
const badgeData = DummyService._makeBadgeData({}, { message: '10k' })
expect(badgeData.text).to.deep.equal(['cat', '10k'])
})
it('preserves an empty label', function() {
const badgeData = DummyService._makeBadgeData(
{},
{ label: '', message: '10k' }
)
expect(badgeData.text).to.deep.equal(['', '10k'])
})
it('applies a numeric service message', function() {
// While a number of badges use this, in the long run we may want
// `render()` to always return a string.
const badgeData = DummyService._makeBadgeData({}, { message: 10 })
expect(badgeData.text).to.deep.equal(['cat', 10])
})
it('applies the service color', function() {
const badgeData = DummyService._makeBadgeData({}, { color: 'red' })
expect(badgeData.color).to.equal('red')
})
it('applies the named logo', function() {
const badgeData = DummyService._makeBadgeData(
{},
{ namedLogo: 'github' }
)
// .not.be.empty for confidence that nothing has changed with `getShieldsIcon()`.
expect(badgeData.logo).to.equal(getShieldsIcon({ name: 'github' })).and
.not.to.be.empty
})
it('applies the named logo with color', function() {
const badgeData = DummyService._makeBadgeData(
{},
{ namedLogo: 'github', logoColor: 'blue' }
)
expect(badgeData.logo).to.equal(
getShieldsIcon({ name: 'github', color: 'blue' })
).and.not.to.be.empty
})
it('applies the logo width', function() {
const badgeData = DummyService._makeBadgeData(
{},
{ namedLogo: 'github', logoWidth: 275 }
)
expect(badgeData.logoWidth).to.equal(275)
})
it('applies the logo position', function() {
const badgeData = DummyService._makeBadgeData(
{},
{ namedLogo: 'github', logoPosition: -10 }
)
expect(badgeData.logoPosition).to.equal(-10)
})
it('applies the service label color', function() {
const badgeData = DummyService._makeBadgeData({}, { labelColor: 'red' })
expect(badgeData.labelColor).to.equal('red')
})
})
describe('Defaults', function() {
it('uses the default label', function() {
const badgeData = DummyService._makeBadgeData({}, {})
expect(badgeData.text).to.deep.equal(['cat', 'n/a'])
})
it('uses the default color', function() {
const badgeData = DummyService._makeBadgeData({}, {})
expect(badgeData.color).to.equal('lightgrey')
})
it('provides no default label color', function() {
const badgeData = DummyService._makeBadgeData({}, {})
expect(badgeData.labelColor).to.be.undefined
})
it('when not a social badge, ignores the default named logo', function() {
const badgeData = DummyService._makeBadgeData({}, {})
expect(badgeData.logo).to.be.undefined
})
it('when a social badge, uses the default named logo', function() {
const badgeData = DummyService._makeBadgeData({ style: 'social' }, {})
expect(badgeData.logo).to.equal(getShieldsIcon({ name: 'appveyor' }))
.and.not.be.empty
})
})
})
describe('ScoutCamp integration', function() {
const expectedRouteRegex = /^\/foo\/([^/]+?)\.(svg|png|gif|jpg|json)$/

View File

@@ -0,0 +1,151 @@
'use strict'
const {
decodeDataUrlFromQueryParam,
prepareNamedLogo,
} = require('../../lib/logos')
const { toArray } = require('../../lib/badge-data')
const { svg2base64 } = require('../../lib/svg-helpers')
const coalesce = require('./coalesce')
// Translate modern badge data to the legacy schema understood by the badge
// maker. Allow the user to override the label, color, logo, etc. through the
// query string. Provide support for most badge options via `serviceData` so
// the Endpoint badge can specify logos and colors, though allow that the
// user's logo or color to take precedence. A notable exception is the case of
// errors. When the service specifies that an error has occurred, the user's
// requested color does not override the error color.
//
// Logos are resolved in this manner:
//
// 1. When `?logo=` contains the name of one of the Shields logos, or contains
// base64-encoded SVG, that logo is used. In the case of a named logo, when
// a `&logoColor=` is specified, that color is used. Otherwise the default
// color is used. `logoColor` will not be applied to a custom
// (base64-encoded) logo; if a custom color is desired the logo should be
// recolored prior to making the request. The appearance of the logo can be
// customized using `logoWidth`, and in the case of the popout badge,
// `logoPosition`. When `?logo=` is specified, any logo-related parameters
// specified dynamically by the service, or by default in the service, are
// ignored.
// 2. The second precedence is the dynamic logo returned by a service. This is
// used only by the Endpoint badge. The `logoColor` can be overridden by the
// query string.
// 3. In the case of the `social` style only, the last precedence is the
// service's default logo. The `logoColor` can be overridden by the query
// string.
module.exports = function coalesceBadge(
overrides,
serviceData,
// These two parameters were kept separate to make tests clearer.
defaultBadgeData,
{ category, _cacheLength: defaultCacheSeconds } = {}
) {
const {
style: overrideStyle,
label: overrideLabel,
logoColor: overrideLogoColor,
link: overrideLink,
} = overrides
// Scoutcamp converts numeric query params to numbers. Convert them back.
let {
colorB: overrideColor,
colorA: overrideLabelColor,
logoWidth: overrideLogoWidth,
logoPosition: overrideLogoPosition,
} = overrides
if (typeof overrideColor === 'number') {
overrideColor = `${overrideColor}`
}
if (typeof overrideLabelColor === 'number') {
overrideLabelColor = `${overrideLabelColor}`
}
overrideLogoWidth = +overrideLogoWidth || undefined
overrideLogoPosition = +overrideLogoPosition || undefined
// `?logo=` could be a named logo or encoded svg. Split up these cases.
const overrideLogoSvgBase64 = decodeDataUrlFromQueryParam(overrides.logo)
const overrideNamedLogo = overrideLogoSvgBase64 ? undefined : overrides.logo
const {
isError,
label: serviceLabel,
message: serviceMessage,
color: serviceColor,
labelColor: serviceLabelColor,
logoSvg: serviceLogoSvg,
namedLogo: serviceNamedLogo,
logoColor: serviceLogoColor,
logoWidth: serviceLogoWidth,
logoPosition: serviceLogoPosition,
link: serviceLink,
cacheSeconds: serviceCacheSeconds,
style: serviceStyle,
} = serviceData
const serviceLogoSvgBase64 = serviceLogoSvg
? svg2base64(serviceLogoSvg)
: undefined
const {
color: defaultColor,
namedLogo: defaultNamedLogo,
label: defaultLabel,
labelColor: defaultLabelColor,
} = defaultBadgeData
const style = coalesce(overrideStyle, serviceStyle)
const namedLogoSvgBase64 = prepareNamedLogo({
name: coalesce(
overrideNamedLogo,
serviceNamedLogo,
style === 'social' ? defaultNamedLogo : undefined
),
color: coalesce(
overrideLogoColor,
// If the logo has been overridden it does not make sense to inherit
// the color.
overrideNamedLogo ? undefined : serviceLogoColor
),
style,
})
return {
text: [
// Use `coalesce()` to support empty labels and messages, as in the
// static badge.
coalesce(overrideLabel, serviceLabel, defaultLabel, category),
coalesce(serviceMessage, 'n/a'),
],
color: coalesce(
// In case of an error, disregard user's color override.
isError ? undefined : overrideColor,
serviceColor,
defaultColor,
'lightgrey'
),
labelColor: coalesce(
// In case of an error, disregard user's color override.
isError ? undefined : overrideLabelColor,
serviceLabelColor,
defaultLabelColor
),
template: style,
logo: coalesce(
overrideLogoSvgBase64,
serviceLogoSvgBase64,
namedLogoSvgBase64
),
logoWidth: coalesce(
overrideLogoWidth,
// If the logo has been overridden it does not make sense to inherit
// the width or position.
overrideNamedLogo ? undefined : serviceLogoWidth
),
logoPosition: coalesce(
overrideLogoPosition,
overrideNamedLogo ? undefined : serviceLogoPosition
),
links: toArray(overrideLink || serviceLink),
cacheLengthSeconds: coalesce(serviceCacheSeconds, defaultCacheSeconds),
}
}

View File

@@ -0,0 +1,257 @@
'use strict'
const { expect } = require('chai')
const { getShieldsIcon } = require('../../lib/logos')
const coalesceBadge = require('./coalesce-badge')
describe('coalesceBadge', function() {
describe('Label', function() {
it('uses the default label', function() {
expect(coalesceBadge({}, {}, { label: 'heyo' }).text).to.deep.equal([
'heyo',
'n/a',
])
})
// This behavior isn't great and we might want to remove it.
it('uses the category as a default label', function() {
expect(coalesceBadge({}, {}, {}, { category: 'cat' }).text).to.deep.equal(
['cat', 'n/a']
)
})
it('preserves an empty label', function() {
expect(
coalesceBadge({}, { label: '', message: '10k' }, {}).text
).to.deep.equal(['', '10k'])
})
it('overrides the label', function() {
expect(
coalesceBadge({ label: 'purr count' }, { label: 'purrs' }, {}).text
).to.deep.equal(['purr count', 'n/a'])
})
})
describe('Message', function() {
it('applies the service message', function() {
expect(coalesceBadge({}, { message: '10k' }, {}).text).to.deep.equal([
undefined,
'10k',
])
})
it('applies a numeric service message', function() {
// While a number of badges use this, in the long run we may want
// `render()` to always return a string.
expect(coalesceBadge({}, { message: 10 }, {}).text).to.deep.equal([
undefined,
10,
])
})
})
describe('Right color', function() {
it('uses the default color', function() {
expect(coalesceBadge({}, {}, {}).color).to.equal('lightgrey')
})
it('overrides the color', function() {
expect(
coalesceBadge({ colorB: '10ADED' }, { color: 'red' }, {}).color
).to.equal('10ADED')
})
context('In case of an error', function() {
it('does not override the color', function() {
expect(
coalesceBadge(
{ colorB: '10ADED' },
{ isError: true, color: 'lightgray' },
{}
).color
).to.equal('lightgray')
})
})
it('applies the service color', function() {
expect(coalesceBadge({}, { color: 'red' }, {}).color).to.equal('red')
})
})
describe('Left color', function() {
it('provides no default label color', function() {
expect(coalesceBadge({}, {}, {}).labelColor).to.be.undefined
})
it('applies the service label color', function() {
expect(coalesceBadge({}, { labelColor: 'red' }, {}).labelColor).to.equal(
'red'
)
})
it('overrides the label color', function() {
expect(
coalesceBadge({ colorA: '42f483' }, { color: 'green' }, {}).labelColor
).to.equal('42f483')
})
it('converts a query-string numeric color to a string', function() {
expect(
coalesceBadge(
// Scoutcamp converts numeric query params to numbers.
{ colorB: 123 },
{ color: 'green' },
{}
).color
).to.equal('123')
})
})
describe('Named logos', function() {
it('when not a social badge, ignores the default named logo', function() {
expect(coalesceBadge({}, {}, { namedLogo: 'appveyor' }).logo).to.be
.undefined
})
it('when a social badge, uses the default named logo', function() {
// .not.be.empty for confidence that nothing has changed with `getShieldsIcon()`.
expect(
coalesceBadge({ style: 'social' }, {}, { namedLogo: 'appveyor' }).logo
).to.equal(getShieldsIcon({ name: 'appveyor' })).and.not.be.empty
})
it('applies the named logo', function() {
expect(coalesceBadge({}, { namedLogo: 'github' }, {}).logo).to.equal(
getShieldsIcon({ name: 'github' })
).and.not.to.be.empty
})
it('applies the named logo with color', function() {
expect(
coalesceBadge({}, { namedLogo: 'github', logoColor: 'blue' }, {}).logo
).to.equal(getShieldsIcon({ name: 'github', color: 'blue' })).and.not.to
.be.empty
})
it('overrides the logo', function() {
expect(
coalesceBadge({ logo: 'github' }, { namedLogo: 'appveyor' }, {}).logo
).to.equal(getShieldsIcon({ name: 'github' })).and.not.be.empty
})
it('overrides the logo with a color', function() {
expect(
coalesceBadge(
{ logo: 'github', logoColor: 'blue' },
{ namedLogo: 'appveyor' },
{}
).logo
).to.equal(getShieldsIcon({ name: 'github', color: 'blue' })).and.not.be
.empty
})
it("when the logo is overridden, it ignores the service's logo color, position, and width", function() {
expect(
coalesceBadge(
{ logo: 'github' },
{
namedLogo: 'appveyor',
logoColor: 'red',
logoPosition: -3,
logoWidth: 100,
},
{}
).logo
).to.equal(getShieldsIcon({ name: 'github' })).and.not.be.empty
})
it("overrides the service logo's color", function() {
expect(
coalesceBadge(
{ logoColor: 'blue' },
{ namedLogo: 'github', logoColor: 'red' },
{}
).logo
).to.equal(getShieldsIcon({ name: 'github', color: 'blue' })).and.not.be
.empty
})
})
describe('Custom logos', function() {
it('overrides the logo with custom svg', function() {
const logoSvg = 'data:image/svg+xml;base64,PHN2ZyB4bWxu'
expect(
coalesceBadge({ logo: logoSvg }, { namedLogo: 'appveyor' }, {}).logo
).to.equal(logoSvg)
})
it('ignores the color when custom svg is provided', function() {
const logoSvg = 'data:image/svg+xml;base64,PHN2ZyB4bWxu'
expect(
coalesceBadge(
{ logo: logoSvg, logoColor: 'brightgreen' },
{ namedLogo: 'appveyor' },
{}
).logo
).to.equal(logoSvg)
})
})
describe('Logo width', function() {
it('overrides the logoWidth', function() {
expect(coalesceBadge({ logoWidth: 20 }, {}, {}).logoWidth).to.equal(20)
})
it('applies the logo width', function() {
expect(
coalesceBadge({}, { namedLogo: 'github', logoWidth: 275 }, {}).logoWidth
).to.equal(275)
})
})
describe('Logo position', function() {
it('overrides the logoPosition', function() {
expect(
coalesceBadge({ logoPosition: -10 }, {}, {}).logoPosition
).to.equal(-10)
})
it('applies the logo position', function() {
expect(
coalesceBadge({}, { namedLogo: 'github', logoPosition: -10 }, {})
.logoPosition
).to.equal(-10)
})
})
describe('Links', function() {
it('overrides the links', function() {
expect(
coalesceBadge(
{ link: 'https://circleci.com/gh/badges/daily-tests' },
{
link:
'https://circleci.com/workflow-run/184ef3de-4836-4805-a2e4-0ceba099f92d',
},
{}
).links
).to.deep.equal(['https://circleci.com/gh/badges/daily-tests'])
})
})
describe('Style', function() {
it('overrides the template', function() {
expect(coalesceBadge({ style: 'pill' }, {}, {}).template).to.equal('pill')
})
})
describe('Cache length', function() {
it('overrides the cache length', function() {
expect(
coalesceBadge({ style: 'pill' }, { cacheSeconds: 123 }, {})
.cacheLengthSeconds
).to.equal(123)
})
})
})

View File

@@ -2,6 +2,7 @@
const Joi = require('joi')
const pathToRegexp = require('path-to-regexp')
const coalesceBadge = require('./coalesce-badge')
const optionalObjectOfKeyValues = Joi.object().pattern(
/./,
@@ -142,7 +143,12 @@ function transformExample(inExample, index, ServiceClass) {
const {
text: [label, message],
color,
} = ServiceClass._makeBadgeData({}, staticPreview)
} = coalesceBadge(
{},
staticPreview,
ServiceClass.defaultBadgeData,
ServiceClass
)
preview = { label, message: `${message}`, color }
} else {
preview = {