Convert sponsor.js to sponsor.js, update tests to be more targeted. (#4199)

* Convert sponsor.js to sponsor.js, update tests to be more targeted.

Co-authored-by: Rubén Rincón Blanco <ruben@rinconblanco.es>
This commit is contained in:
Matt Godbolt
2022-10-25 18:20:16 -05:00
committed by GitHub
parent 1e0543fbbb
commit c8e8a80760
4 changed files with 132 additions and 84 deletions

View File

@@ -0,0 +1,49 @@
// Copyright (c) 2022, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
export type Sponsor = {
name: string;
description?: string;
img?: string;
icon?: string;
icon_dark?: string;
url?: string;
onclick: string;
priority: number;
topIcon: boolean;
sideBySide: boolean;
statsId?: string;
};
export type Level = {
name: string;
description: string;
class?: string;
sponsors: Sponsor[];
};
export type Sponsors = {
icons: Sponsor[];
levels: Level[];
};

View File

@@ -24,40 +24,39 @@
import yaml from 'yaml';
function namify(mapOrString) {
if (typeof mapOrString == 'string') return {name: mapOrString};
return mapOrString;
import {Sponsor, Sponsors} from './sponsors.interfaces';
export function parse(mapOrString: Record<string, any> | string): Sponsor {
if (typeof mapOrString == 'string') mapOrString = {name: mapOrString};
return {
name: mapOrString.name,
description: mapOrString.description,
url: mapOrString.url,
onclick: mapOrString.url ? `window.onSponsorClick(${JSON.stringify(mapOrString.url)});` : '',
img: mapOrString.img,
icon: mapOrString.icon || mapOrString.img,
icon_dark: mapOrString.icon_dark,
topIcon: !!mapOrString.topIcon,
sideBySide: !!mapOrString.sideBySide,
priority: mapOrString.priority || 0,
statsId: mapOrString.statsId,
};
}
function clickify(sponsor) {
if (sponsor.url) {
sponsor.onclick = `window.onSponsorClick(${JSON.stringify(sponsor)});`;
}
return sponsor;
}
function getIconUrl(sponsor) {
const icon = sponsor.icon || sponsor.img;
if (icon) {
sponsor.icon = icon;
}
return sponsor;
}
function compareSponsors(lhs, rhs) {
const lhsPrio = lhs.priority || 0;
const rhsPrio = rhs.priority || 0;
function compareSponsors(lhs: Sponsor, rhs: Sponsor): number {
const lhsPrio = lhs.priority;
const rhsPrio = rhs.priority;
if (lhsPrio !== rhsPrio) return rhsPrio - lhsPrio;
return lhs.name.localeCompare(rhs.name);
}
export function loadSponsorsFromString(stringConfig) {
export function loadSponsorsFromString(stringConfig: string): Sponsors {
const sponsorConfig = yaml.parse(stringConfig);
sponsorConfig.icons = [];
for (const level of sponsorConfig.levels) {
for (const required of ['name', 'description'])
for (const required of ['name', 'description', 'sponsors'])
if (!level[required]) throw new Error(`Level is missing '${required}'`);
level.sponsors = level.sponsors.map(namify).map(clickify).map(getIconUrl).sort(compareSponsors);
level.sponsors = level.sponsors.map(parse).sort(compareSponsors);
sponsorConfig.icons.push(...level.sponsors.filter(sponsor => sponsor.topIcon && sponsor.icon));
}
return sponsorConfig;

View File

@@ -672,15 +672,15 @@ function start() {
$('[name="editor-btn-toolbar"]').addClass('d-none');
}
window.onSponsorClick = function (sponsor) {
window.onSponsorClick = function (sponsorUrl) {
analytics.proxy('send', {
hitType: 'event',
eventCategory: 'Sponsors',
eventAction: 'click',
eventLabel: sponsor.url,
eventLabel: sponsorUrl,
transport: 'beacon',
});
window.open(sponsor.url);
window.open(sponsorUrl);
};
if (options.pageloadUrl) {

View File

@@ -22,9 +22,54 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import {loadSponsorsFromString} from '../lib/sponsors';
import {loadSponsorsFromString, parse} from '../lib/sponsors';
import {should} from './utils';
describe('Sponsors', () => {
it('should expand names to objects', () => {
parse('moo').name.should.eq('moo');
});
it('should handle just names', () => {
parse({name: 'moo'}).name.should.eq('moo');
});
it('should default empty params', () => {
const obj = parse('moo');
should.equal(obj.description, undefined);
should.equal(obj.url, undefined);
obj.onclick.should.eq('');
should.equal(obj.img, undefined);
should.equal(obj.icon, undefined);
should.equal(obj.icon_dark, undefined);
obj.topIcon.should.be.false;
obj.sideBySide.should.be.false;
should.equal(obj.statsId, undefined);
});
it('should pass through descriptions', () => {
parse({name: 'moo', description: 'desc'}).description.should.eq('desc');
});
it('should pass through icons', () => {
parse({name: 'bob', icon: 'icon'}).icon.should.eq('icon');
});
it('should pick icons over images', () => {
parse({name: 'bob', img: 'img', icon: 'icon'}).icon.should.eq('icon');
});
it('should pick icons if not img', () => {
parse({name: 'bob', img: 'img'}).icon.should.eq('img');
});
it('should pick dark icons if specified', () => {
parse({name: 'bob', icon: 'icon', icon_dark: 'icon_dark'}).icon_dark.should.eq('icon_dark');
});
it('should handle topIcons', () => {
parse({name: 'bob', topIcon: true}).topIcon.should.be.true;
});
it('should handle clicks', () => {
parse({
name: 'bob',
url: 'https://some.host/click',
}).onclick.should.eq('window.onSponsorClick("https://some.host/click");');
});
it('should load a simple example', () => {
const sample = loadSponsorsFromString(`
---
@@ -49,19 +94,6 @@ levels:
sample.levels[1].name.should.eq('Patreons');
});
it('should expand names to objects', () => {
const folks = loadSponsorsFromString(`
---
levels:
- name: a
description: d
sponsors:
- Just a string
- name: An object
`).levels[0].sponsors;
folks.should.deep.equalInAnyOrder([{name: 'An object'}, {name: 'Just a string'}]);
});
it('should sort sponsors by name', () => {
const peeps = loadSponsorsFromString(`
---
@@ -74,7 +106,7 @@ levels:
- A
- B
`).levels[0].sponsors;
peeps.should.deep.equals([{name: 'A'}, {name: 'B'}, {name: 'C'}, {name: 'D'}]);
peeps.map(sponsor => sponsor.name).should.deep.equals(['A', 'B', 'C', 'D']);
});
it('should sort sponsors by priority then name', () => {
const peeps = loadSponsorsFromString(`
@@ -90,44 +122,15 @@ levels:
- name: B
priority: 50
`).levels[0].sponsors;
peeps.should.deep.equals([
{name: 'D', priority: 100},
{name: 'B', priority: 50},
{name: 'C', priority: 50},
]);
});
it('should pick icon over img', () => {
const things = loadSponsorsFromString(`
---
levels:
- name: a
description: d
sponsors:
- name: one
img: image
- name: two
img: not_an_icon
icon: icon
`).levels[0].sponsors;
things.should.deep.equalInAnyOrder([
{name: 'one', icon: 'image', img: 'image'},
{name: 'two', icon: 'icon', img: 'not_an_icon'},
]);
});
it('should pick up dark icon if set', () => {
const things = loadSponsorsFromString(`
---
levels:
- name: a
description: d
sponsors:
- name: batman
img: not_an_icon
icon: icon
icon_dark: dark
`).levels[0].sponsors;
things.should.deep.equalInAnyOrder([{name: 'batman', icon: 'icon', icon_dark: 'dark', img: 'not_an_icon'}]);
peeps
.map(sponsor => {
return {name: sponsor.name, priority: sponsor.priority};
})
.should.deep.equals([
{name: 'D', priority: 100},
{name: 'B', priority: 50},
{name: 'C', priority: 50},
]);
});
it('should pick out the top level icons', () => {
@@ -154,9 +157,6 @@ levels:
- name: five
topIcon: true
`).icons;
icons.should.deep.equalInAnyOrder([
{name: 'one', icon: 'pick_me', img: 'pick_me', topIcon: true},
{name: 'four', icon: 'pick_me_also', img: 'pick_me_also', topIcon: true},
]);
icons.map(s => s.name).should.deep.equals(['one', 'four']);
});
});