Compare commits

..

1 Commits

Author SHA1 Message Date
chris48s
57036a34b0 migrate integration tests to GH actions 2022-09-22 20:44:43 +01:00
72 changed files with 2431 additions and 3562 deletions

View File

@@ -9,23 +9,23 @@
"version": "0.0.0",
"license": "CC0",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1"
"@actions/core": "^1.9.1",
"@actions/github": "^5.0.3"
}
},
"node_modules/@actions/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz",
"integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
}
},
"node_modules/@actions/github": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
"integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.3.tgz",
"integrity": "sha512-myjA/pdLQfhUGLtRZC/J4L1RXOG4o6aYdiEq+zr5wVVKljzbFld+xv10k1FX6IkIJtNxbAq44BdwSNpQ015P0A==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"@octokit/core": "^3.6.0",
@@ -235,18 +235,18 @@
},
"dependencies": {
"@actions/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz",
"integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==",
"requires": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
}
},
"@actions/github": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
"integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.3.tgz",
"integrity": "sha512-myjA/pdLQfhUGLtRZC/J4L1RXOG4o6aYdiEq+zr5wVVKljzbFld+xv10k1FX6IkIJtNxbAq44BdwSNpQ015P0A==",
"requires": {
"@actions/http-client": "^2.0.1",
"@octokit/core": "^3.6.0",

View File

@@ -10,7 +10,7 @@
"author": "chris48s",
"license": "CC0",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1"
"@actions/core": "^1.9.1",
"@actions/github": "^5.0.3"
}
}

View File

@@ -1,7 +1,5 @@
name: Auto close
on:
pull_request_target:
types: [opened]
on: pull_request_target
permissions:
pull-requests: write

View File

@@ -1,7 +1,5 @@
name: 'Dependency Review'
on:
pull_request:
types: [opened, edited, reopened, synchronize]
on: [pull_request]
jobs:
enforce-dependency-review:

View File

@@ -1,17 +1,13 @@
name: Integration@node 17
on:
pull_request:
types: [opened, edited, reopened, synchronize]
push:
branches-ignore:
- 'gh-pages'
- 'dependabot/**'
jobs:
test-integration-17:
runs-on: ubuntu-latest
env:
PAT_EXISTS: ${{ secrets.GH_PAT != '' }}
services:
redis:
@@ -35,14 +31,7 @@ jobs:
env:
NPM_CONFIG_ENGINE_STRICT: 'false'
- name: Integration Tests (with PAT)
if: ${{ env.PAT_EXISTS == 'true' }}
uses: ./.github/actions/integration-tests
with:
github-token: '${{ secrets.GH_PAT }}'
- name: Integration Tests (with workflow token)
if: ${{ env.PAT_EXISTS == 'false' }}
- name: Integration Tests
uses: ./.github/actions/integration-tests
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'

View File

@@ -1,17 +1,13 @@
name: Integration
on:
pull_request:
types: [opened, edited, reopened, synchronize]
push:
branches-ignore:
- 'gh-pages'
- 'dependabot/**'
jobs:
test-integration:
runs-on: ubuntu-latest
env:
PAT_EXISTS: ${{ secrets.GH_PAT != '' }}
services:
redis:
@@ -33,14 +29,7 @@ jobs:
with:
node-version: 16
- name: Integration Tests (with PAT)
if: ${{ env.PAT_EXISTS == 'true' }}
uses: ./.github/actions/integration-tests
with:
github-token: '${{ secrets.GH_PAT }}'
- name: Integration Tests (with workflow token)
if: ${{ env.PAT_EXISTS == 'false' }}
- name: Integration Tests
uses: ./.github/actions/integration-tests
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'

View File

@@ -1,11 +1,9 @@
name: Lint
on:
pull_request:
types: [opened, edited, reopened, synchronize]
push:
branches-ignore:
- 'gh-pages'
- 'dependabot/**'
jobs:
test-lint:

View File

@@ -1,11 +1,9 @@
name: Main@node 17
on:
pull_request:
types: [opened, edited, reopened, synchronize]
push:
branches-ignore:
- 'gh-pages'
- 'dependabot/**'
jobs:
test-main-17:

View File

@@ -1,11 +1,9 @@
name: Main
on:
pull_request:
types: [opened, edited, reopened, synchronize]
push:
branches-ignore:
- 'gh-pages'
- 'dependabot/**'
jobs:
test-main:

View File

@@ -1,11 +1,9 @@
name: Package CLI
on:
pull_request:
types: [opened, edited, reopened, synchronize]
push:
branches-ignore:
- 'gh-pages'
- 'dependabot/**'
# Smoke test (render a badge with the CLI) with only the package
# dependencies installed.

View File

@@ -1,11 +1,9 @@
name: Package Library
on:
pull_request:
types: [opened, edited, reopened, synchronize]
push:
branches-ignore:
- 'gh-pages'
- 'dependabot/**'
jobs:
test-package-lib:

View File

@@ -4,39 +4,6 @@ Note: this changelog is for the shields.io server. The changelog for the badge-m
---
## server-2022-11-01
- [Ansible] Add collection badge [#8578](https://github.com/badges/shields/issues/8578)
- [VisualStudioMarketplace] Add support to prerelease extensions version (Issue #8207) [#8561](https://github.com/badges/shields/issues/8561)
- feat: add [GitlabLastCommit] service [#8508](https://github.com/badges/shields/issues/8508)
- fix [swagger] service tests (allow 0 items in array) [#8564](https://github.com/badges/shields/issues/8564)
- fix codecov badge for non-default branch [#8565](https://github.com/badges/shields/issues/8565)
- Add [GitHubLastCommit] by committer badge [#8537](https://github.com/badges/shields/issues/8537)
- [GitHubReleaseDate] - published_at field [#8543](https://github.com/badges/shields/issues/8543)
- Fix [Testspace] with new "untested" value in case_counts array [#8544](https://github.com/badges/shields/issues/8544)
- fix: Support WAITING status for GitHub deployments [#8521](https://github.com/badges/shields/issues/8521)
- [Whatpulse] badge for a user and for a team [#8466](https://github.com/badges/shields/issues/8466)
- deprecate [pkgreview] service [#8499](https://github.com/badges/shields/issues/8499)
- Dependency updates
## server-2022-10-08
- deprecate [criterion] service [#8501](https://github.com/badges/shields/issues/8501)
- fix formatRelativeDate error handling; run [date] [#8497](https://github.com/badges/shields/issues/8497)
- allow/validate bitbucket_username / bitbucket_password in private config schema [#8472](https://github.com/badges/shields/issues/8472)
- fix [pub] points badge test and example [#8498](https://github.com/badges/shields/issues/8498)
- feat: add [GitlabLanguageCount] service [#8377](https://github.com/badges/shields/issues/8377)
- [GitHubGistStars] add GitHub Gist Stars [#8471](https://github.com/badges/shields/issues/8471)
- fix display/search of CII badge examples [#8473](https://github.com/badges/shields/issues/8473)
- feat: add 2022 support to GitHub Hacktoberfest [#8468](https://github.com/badges/shields/issues/8468)
- fix [GitLabCoverage] subgroup bug [#8401](https://github.com/badges/shields/issues/8401)
- implement ruby gems-specific version sort/color functions [#8434](https://github.com/badges/shields/issues/8434)
- Add `rc` to pre-release identifiers [#8435](https://github.com/badges/shields/issues/8435)
- add [GitHub] Number of commits between branches/tags/commits [#8394](https://github.com/badges/shields/issues/8394)
- add [Packagist] dependency version [#8371](https://github.com/badges/shields/issues/8371)
- fix Docker build status invalid response data bug [#8392](https://github.com/badges/shields/issues/8392)
- Dependency updates
## server-2022-09-04
- fix frontend compile for users running on Windows [#8350](https://github.com/badges/shields/issues/8350)

View File

@@ -134,7 +134,7 @@ Prettier before a commit by default.
When adding or changing a service [please write tests][service-tests], and ensure the [title of your Pull Requests follows the required conventions](#running-service-tests-in-pull-requests) to ensure your tests are executed.
When changing other code, please add unit tests.
To run the integration tests, you must have Redis installed and in your PATH.
To run the integration tests, you must have redis installed and in your PATH.
Use `brew install redis`, `yum install redis`, etc. The test runner will
start the server automatically.

View File

@@ -169,8 +169,6 @@ const privateConfigSchema = Joi.object({
jenkins_pass: Joi.string(),
jira_user: Joi.string(),
jira_pass: Joi.string(),
bitbucket_username: Joi.string(),
bitbucket_password: Joi.string(),
bitbucket_server_username: Joi.string(),
bitbucket_server_password: Joi.string(),
librariesio_tokens: Joi.arrayFromString().items(Joi.string()),

4404
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,32 +21,31 @@
"url": "https://github.com/badges/shields"
},
"dependencies": {
"@fontsource/lato": "^4.5.10",
"@fontsource/lekton": "^4.5.11",
"@fontsource/lato": "^4.5.9",
"@fontsource/lekton": "^4.5.10",
"@renovate/pep440": "^1.0.0",
"@renovatebot/ruby-semver": "^1.1.6",
"@sentry/node": "^7.17.2",
"@sentry/node": "^7.13.0",
"@shields_io/camp": "^18.1.1",
"badge-maker": "file:badge-maker",
"bytes": "^3.1.2",
"camelcase": "^7.0.0",
"chalk": "^5.1.2",
"chalk": "^5.0.1",
"check-node-version": "^4.2.1",
"cloudflare-middleware": "^1.0.4",
"config": "^3.3.8",
"cross-env": "^7.0.3",
"dayjs": "^1.11.6",
"dayjs": "^1.11.5",
"decamelize": "^3.2.0",
"emojic": "^1.1.17",
"escape-string-regexp": "^4.0.0",
"fast-xml-parser": "^4.0.11",
"fast-xml-parser": "^4.0.10",
"glob": "^8.0.3",
"global-agent": "^3.0.0",
"got": "^12.5.2",
"got": "^12.4.1",
"graphql": "^15.6.1",
"graphql-tag": "^2.12.6",
"ioredis": "5.2.3",
"joi": "17.6.4",
"joi": "17.6.0",
"joi-extension-semver": "5.0.0",
"js-yaml": "^4.1.0",
"jsonpath": "~1.1.1",
@@ -61,8 +60,8 @@
"prom-client": "^14.1.0",
"qs": "^6.11.0",
"query-string": "^7.1.1",
"semver": "~7.3.8",
"simple-icons": "7.17.0",
"semver": "~7.3.7",
"simple-icons": "7.11.0",
"webextension-store-meta": "^1.0.5",
"xmldom": "~0.6.0",
"xpath": "~0.0.32"
@@ -142,7 +141,7 @@
]
},
"devDependencies": {
"@babel/core": "^7.19.6",
"@babel/core": "^7.19.1",
"@babel/polyfill": "^7.12.1",
"@babel/register": "7.18.9",
"@istanbuljs/schema": "^0.1.3",
@@ -150,13 +149,13 @@
"@types/chai": "^4.3.3",
"@types/lodash.debounce": "^4.0.7",
"@types/lodash.groupby": "^4.6.7",
"@types/mocha": "^10.0.0",
"@types/mocha": "^9.1.1",
"@types/node": "^16.7.10",
"@types/react-helmet": "^6.1.5",
"@types/react-modal": "^3.13.1",
"@types/react-select": "^4.0.17",
"@types/styled-components": "5.1.26",
"@typescript-eslint/eslint-plugin": "^5.41.0",
"@typescript-eslint/eslint-plugin": "^5.37.0",
"@typescript-eslint/parser": "^5.30.7",
"babel-plugin-inline-react-svg": "^2.0.1",
"babel-preset-gatsby": "^2.22.0",
@@ -168,10 +167,10 @@
"chai-string": "^1.4.0",
"child-process-promise": "^2.2.1",
"clipboard-copy": "^4.0.1",
"concurrently": "^7.5.0",
"cypress": "^10.11.0",
"cypress-wait-for-stable-dom": "^0.1.0",
"danger": "^11.1.4",
"concurrently": "^7.4.0",
"cypress": "^10.8.0",
"cypress-wait-for-stable-dom": "^0.0.3",
"danger": "^11.1.2",
"danger-plugin-no-test-shortcuts": "^2.0.0",
"deepmerge": "^4.2.2",
"eslint": "^7.32.0",
@@ -182,22 +181,22 @@
"eslint-plugin-chai-friendly": "^0.7.2",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsdoc": "^39.3.25",
"eslint-plugin-jsdoc": "^39.3.6",
"eslint-plugin-mocha": "^10.1.0",
"eslint-plugin-no-extension-in-require": "^0.2.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.2.0",
"eslint-plugin-react": "^7.31.10",
"eslint-plugin-react": "^7.31.8",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-sort-class-members": "^1.15.2",
"fetch-ponyfill": "^7.1.0",
"form-data": "^4.0.0",
"gatsby": "4.23.1",
"gatsby": "4.23.0",
"gatsby-plugin-catch-links": "^4.19.0",
"gatsby-plugin-page-creator": "^4.24.0",
"gatsby-plugin-page-creator": "^4.22.0",
"gatsby-plugin-react-helmet": "^5.22.0",
"gatsby-plugin-remove-trailing-slashes": "^4.9.0",
"gatsby-plugin-styled-components": "^5.24.0",
"gatsby-plugin-styled-components": "^5.19.0",
"gatsby-plugin-typescript": "^4.22.0",
"humanize-string": "^2.1.0",
"icedfrisby": "4.0.0",
@@ -208,38 +207,38 @@
"lint-staged": "^13.0.3",
"lodash.debounce": "^4.0.8",
"lodash.difference": "^4.5.0",
"minimist": "^1.2.7",
"mocha": "^10.1.0",
"minimist": "^1.2.6",
"mocha": "^9.2.2",
"mocha-env-reporter": "^4.0.0",
"mocha-junit-reporter": "^2.1.1",
"mocha-junit-reporter": "^2.0.2",
"mocha-yaml-loader": "^1.0.3",
"nock": "13.2.9",
"node-mocks-http": "^1.11.0",
"nodemon": "^2.0.20",
"npm-run-all": "^4.1.5",
"open-cli": "^7.1.0",
"open-cli": "^7.0.1",
"portfinder": "^1.0.32",
"prettier": "2.7.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-error-overlay": "^6.0.11",
"react-helmet": "^6.1.0",
"react-modal": "^3.16.1",
"react-modal": "^3.15.1",
"react-pose": "^4.0.10",
"react-select": "^4.3.1",
"read-all-stdin-sync": "^1.0.5",
"redis-server": "^1.2.2",
"rimraf": "^3.0.2",
"sazerac": "^2.0.0",
"simple-git-hooks": "^2.8.1",
"sinon": "^14.0.1",
"simple-git-hooks": "^2.8.0",
"sinon": "^14.0.0",
"sinon-chai": "^3.7.0",
"snap-shot-it": "^7.9.6",
"start-server-and-test": "1.14.0",
"styled-components": "^5.3.6",
"styled-components": "^5.3.5",
"ts-mocha": "^10.0.0",
"tsd": "^0.24.1",
"typescript": "^4.8.4",
"typescript": "^4.8.3",
"url": "^0.11.0"
},
"engines": {

View File

@@ -1,46 +0,0 @@
import Joi from 'joi'
import { BaseJsonService } from '../index.js'
const ansibleCollectionSchema = Joi.object({
name: Joi.string().required(),
namespace: Joi.object({
name: Joi.string().required(),
}),
}).required()
class AnsibleGalaxyCollectionName extends BaseJsonService {
static category = 'other'
static route = { base: 'ansible/collection', pattern: ':collectionId' }
static examples = [
{
title: 'Ansible Collection',
namedParams: { collectionId: '278' },
staticPreview: this.render({
name: 'community.general',
}),
},
]
static defaultBadgeData = { label: 'collection' }
static render({ name }) {
return { message: name, color: 'blue' }
}
async fetch({ collectionId }) {
const url = `https://galaxy.ansible.com/api/v2/collections/${collectionId}/`
return this._requestJson({
url,
schema: ansibleCollectionSchema,
})
}
async handle({ collectionId }) {
const json = await this.fetch({ collectionId })
const name = `${json.namespace.name}.${json.name}`
return this.constructor.render({ name })
}
}
export { AnsibleGalaxyCollectionName }

View File

@@ -1,14 +0,0 @@
import { ServiceTester } from '../tester.js'
export const t = new ServiceTester({
id: 'AnsibleCollection',
title: 'AnsibleCollection',
pathPrefix: '/ansible/collection',
})
t.create('collection name (valid)')
.get('/278.json')
.expectBadge({ label: 'collection', message: 'community.general' })
t.create('collection name (not found)')
.get('/000.json')
.expectBadge({ label: 'collection', message: 'not found' })

View File

@@ -20,7 +20,7 @@ t.create('pr-raw (not found)')
t.create('pr-raw (private repo)')
.get('/pr-raw/chris48s/example-private-repo.json')
.expectBadge({ label: 'pull requests', message: 'not found' })
.expectBadge({ label: 'pull requests', message: 'private repo' })
t.create('pr (valid)').get('/pr/atlassian/python-bitbucket.json').expectBadge({
label: 'pull requests',
@@ -33,7 +33,7 @@ t.create('pr (not found)')
t.create('pr (private repo)')
.get('/pr/chris48s/example-private-repo.json')
.expectBadge({ label: 'pull requests', message: 'not found' })
.expectBadge({ label: 'pull requests', message: 'private repo' })
t.create('pr (server)')
.get('/pr/project/repo.json?server=https://bitbucket.mydomain.net')

View File

@@ -35,7 +35,7 @@ export default class CIIBestPracticesService extends BaseJsonService {
pattern: ':metric(level|percentage|summary)/:projectId',
}
static examples = [
static exampless = [
{
title: 'CII Best Practices Level',
pattern: 'level/:projectId',

View File

@@ -107,7 +107,7 @@ export default class Codecov extends BaseSvgScrapingService {
async legacyFetch({ vcsName, user, repo, branch, token }) {
// Codecov Docs: https://docs.codecov.io/reference#section-get-a-single-repository
const url = `https://codecov.io/api/${vcsName}/${user}/${repo}${
branch ? `/branch/${branch}` : ''
branch ? `/branches/${branch}` : ''
}`
const { buffer } = await this._request({
url,

View File

@@ -23,7 +23,7 @@ function version(version) {
if (first === 'v') {
first = version[1]
}
if (first === '0' || /alpha|beta|snapshot|dev|pre|rc/i.test(version)) {
if (first === '0' || /alpha|beta|snapshot|dev|pre/i.test(version)) {
return 'orange'
} else {
return 'blue'

View File

@@ -88,7 +88,6 @@ describe('Color formatters', function () {
given('6.0-SNAPSHOT'),
given('1.0.1-dev'),
given('2.1.6-prerelease'),
given('2.1.6-RC1'),
]).expect('orange')
expect(() => version(null)).to.throw(

View File

@@ -1,11 +1,66 @@
import { deprecatedService } from '../index.js'
import Joi from 'joi'
import { BaseJsonService } from '../index.js'
import {
IMPROVED_STATUS,
NOT_FOUND_STATUS,
REGRESSED_STATUS,
NO_CHANGE_STATUS,
} from './constants.js'
export default deprecatedService({
category: 'analysis',
route: {
base: 'criterion',
pattern: ':various*',
},
label: 'criterion',
dateAdded: new Date('2022-10-07'),
})
const schema = Joi.string()
.allow(IMPROVED_STATUS, REGRESSED_STATUS, NO_CHANGE_STATUS)
.required()
/**
* Criterion Badge Service
*
* Support and Contact:
* - https://github.com/chmoder/api.criterion.dev
*
* API Documentation:
* - https://app.swaggerhub.com/apis-docs/chmoder/Criterion.dev
*/
export default class Criterion extends BaseJsonService {
static category = 'analysis'
static route = { base: 'criterion', pattern: ':user/:repo' }
static examples = [
{
title: 'Criterion',
namedParams: {
user: 'chmoder',
repo: 'data_vault',
},
staticPreview: this.render({ status: IMPROVED_STATUS }),
},
]
static defaultBadgeData = { label: 'criterion' }
static render({ status }) {
let statusColor = 'lightgrey'
if (status === IMPROVED_STATUS) {
statusColor = 'brightgreen'
} else if (status === NO_CHANGE_STATUS) {
statusColor = 'green'
} else if (statusColor === REGRESSED_STATUS) {
statusColor = 'red'
}
return {
message: `${status}`,
color: statusColor,
}
}
async handle({ user, repo }) {
const status = await this._requestJson({
url: `https://api.criterion.dev/v1/${user}/${repo}/status`,
errorMessages: { 404: NOT_FOUND_STATUS },
schema,
})
return this.constructor.render({ status })
}
}

View File

@@ -1,11 +1,21 @@
import { ServiceTester } from '../tester.js'
import Joi from 'joi'
import { createServiceTester } from '../tester.js'
import {
IMPROVED_STATUS,
REGRESSED_STATUS,
NO_CHANGE_STATUS,
NOT_FOUND_STATUS,
} from './constants.js'
export const t = await createServiceTester()
export const t = new ServiceTester({
id: 'criterion',
title: 'Criterion',
pathPrefix: '/criterion',
})
const isStatus = Joi.string()
.allow(IMPROVED_STATUS, REGRESSED_STATUS, NOT_FOUND_STATUS, NO_CHANGE_STATUS)
.required()
t.create('Criterion')
t.create('Criterion (valid repo)')
.get('/chmoder/credit_card.json')
.expectBadge({ label: 'criterion', message: 'no longer available' })
.expectBadge({ label: 'criterion', message: isStatus })
t.create('Criterion (not found)')
.get('/chmoder/not-a-repo.json')
.expectBadge({ label: 'criterion', message: NOT_FOUND_STATUS })

View File

@@ -4,10 +4,9 @@ import { BaseJsonService, InvalidResponse, NotFound } from '../index.js'
/**
* Validates that the schema response is what we're expecting.
* The username pattern should match the requirements in the freeCodeCamp
* repository.
* The username pattern should match the freeCodeCamp repository.
*
* @see https://github.com/freeCodeCamp/freeCodeCamp/blob/main/utils/validate.js
* @see https://github.com/freeCodeCamp/freeCodeCamp/blob/main/utils/validate.js#L14
*/
const schema = Joi.object({
entities: Joi.object({

View File

@@ -1,25 +0,0 @@
import { valid, maxSatisfying, prerelease } from '@renovatebot/ruby-semver'
function latest(versions) {
// latest Ruby Gems version, including pre-releases
return maxSatisfying(versions, '>0')
}
function versionColor(version) {
if (!valid(version)) {
return 'lightgrey'
}
version = `${version}`
let first = version[0]
if (first === 'v') {
first = version[1]
}
if (first === '0' || prerelease(version)) {
return 'orange'
}
return 'blue'
}
export { latest, versionColor }

View File

@@ -1,17 +0,0 @@
import { test, given } from 'sazerac'
import { latest, versionColor } from './gem-helpers.js'
describe('Gem helpers', function () {
test(latest, () => {
given(['2.0.0', '2.0.0.beta1']).expect('2.0.0')
given(['2.0.0.beta1', '1.9.0']).expect('2.0.0.beta1')
given(['0.0.1', '0.0.2']).expect('0.0.2')
})
test(versionColor, () => {
given('1.9.0').expect('blue')
given('2.0.0.beta1').expect('orange')
given('0.0.1').expect('orange')
given('v1').expect('lightgrey')
})
})

View File

@@ -1,7 +1,12 @@
import Joi from 'joi'
import { createServiceTester } from '../tester.js'
import { isOrdinalNumber, isOrdinalNumberDaily } from '../test-validators.js'
export const t = await createServiceTester()
const isOrdinalNumber = Joi.string().regex(/^[1-9][0-9]+(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ)$/)
const isOrdinalNumberDaily = Joi.string().regex(
/^[1-9][0-9]*(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ) daily$/
)
t.create('total rank (valid)').get('/rt/rspec-puppet-facts.json').expectBadge({
label: 'rank',
message: isOrdinalNumber,

View File

@@ -1,7 +1,6 @@
import Joi from 'joi'
import { renderVersionBadge } from '../version.js'
import { renderVersionBadge, latest } from '../version.js'
import { BaseJsonService } from '../index.js'
import { latest, versionColor } from './gem-helpers.js'
const schema = Joi.object({
// In most cases `version` will be a SemVer but the registry doesn't
@@ -46,7 +45,7 @@ export default class GemVersion extends BaseJsonService {
static defaultBadgeData = { label: 'gem' }
static render({ version }) {
return renderVersionBadge({ version, versionFormatter: versionColor })
return renderVersionBadge({ version })
}
async fetch({ gem }) {

View File

@@ -7,7 +7,7 @@ import { documentation, transformErrors } from './github-helpers.js'
const greenStates = ['SUCCESS']
const redStates = ['ERROR', 'FAILURE']
const blueStates = ['INACTIVE']
const otherStates = ['IN_PROGRESS', 'QUEUED', 'PENDING', 'NO_STATUS', 'WAITING']
const otherStates = ['IN_PROGRESS', 'QUEUED', 'PENDING', 'NO_STATUS']
const stateToMessageMappings = {
IN_PROGRESS: 'in progress',

View File

@@ -21,12 +21,6 @@ describe('GithubDeployments', function () {
message: 'in progress',
color: undefined,
})
given({
state: 'WAITING',
}).expect({
message: 'waiting',
color: undefined,
})
given({
state: 'NO_STATUS',
}).expect({

View File

@@ -1,113 +0,0 @@
import gql from 'graphql-tag'
import Joi from 'joi'
import { metric } from '../text-formatters.js'
import { NotFound } from '../index.js'
import { GithubAuthV4Service } from './github-auth-service.js'
import { documentation as commonDocumentation } from './github-helpers.js'
const schema = Joi.object({
data: Joi.object({
viewer: Joi.object({
gist: Joi.object({
stargazerCount: Joi.number().required(),
url: Joi.string().required(),
owner: Joi.object({
login: Joi.string().required(),
}).required(),
name: Joi.string().required(),
}).allow(null),
}).required(),
}).required(),
}).required()
const documentation = `${commonDocumentation}
<p>This badge shows the number of stargazers for a gist. Gist id is accepted as input and 'gist not found' is returned if the gist is not found for the given gist id.
</p>`
export default class GithubGistStars extends GithubAuthV4Service {
static category = 'social'
static route = {
base: 'github/stars/gists',
pattern: ':gistId',
}
static examples = [
{
title: 'Github Gist stars',
namedParams: { gistId: '47a4d00457a92aa426dbd48a18776322' },
staticPreview: {
label: this.defaultBadgeData.label,
message: metric(29),
style: 'social',
},
documentation,
},
]
static defaultBadgeData = {
label: 'Stars',
color: 'blue',
namedLogo: 'github',
}
static render({ stargazerCount, url, stargazers }) {
return { message: metric(stargazerCount), link: [url, stargazers] }
}
async fetch({ gistId }) {
const data = await this._requestGraphql({
query: gql`
query ($gistId: String!) {
viewer {
gist(name: $gistId) {
stargazerCount
url
name
owner {
login
}
}
}
}
`,
variables: {
gistId,
},
schema,
})
return data
}
static transform({ data }) {
const {
data: {
viewer: { gist },
},
} = data
if (!gist) {
throw new NotFound({ prettyMessage: 'gist not found' })
}
const {
stargazerCount,
url,
name,
owner: { login },
} = gist
const stargazers = `https://gist.github.com/${login}/${name}/stargazers`
return { stargazerCount, url, stargazers }
}
async handle({ gistId }) {
const data = await this.fetch({ gistId })
const { stargazerCount, url, stargazers } =
await this.constructor.transform({
data,
})
return this.constructor.render({ stargazerCount, url, stargazers })
}
}

View File

@@ -1,25 +0,0 @@
import { createServiceTester } from '../tester.js'
import { isMetric } from '../test-validators.js'
export const t = await createServiceTester()
t.create('Gist Total Stars')
.get('/47a4d00457a92aa426dbd48a18776322.json')
.expectBadge({
label: 'Stars',
message: isMetric,
color: 'blue',
link: [
'https://gist.github.com/47a4d00457a92aa426dbd48a18776322',
'https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322/stargazers',
],
})
t.create('Gist Total Stars (Not Found)')
.get('/invalid-gist-id.json')
.expectBadge({
label: 'Stars',
message: 'gist not found',
color: 'red',
link: [],
})

View File

@@ -60,7 +60,7 @@ export default class GithubHacktoberfestCombinedStatus extends GithubAuthV4Servi
static category = 'issue-tracking'
static route = {
base: 'github/hacktoberfest',
pattern: ':year(2019|2020|2021|2022)/:user/:repo',
pattern: ':year(2019|2020|2021)/:user/:repo',
queryParamSchema,
}
@@ -68,7 +68,7 @@ export default class GithubHacktoberfestCombinedStatus extends GithubAuthV4Servi
{
title: 'GitHub Hacktoberfest combined status',
namedParams: {
year: '2022',
year: '2021',
user: 'snyk',
repo: 'snyk',
},
@@ -82,7 +82,7 @@ export default class GithubHacktoberfestCombinedStatus extends GithubAuthV4Servi
{
title: 'GitHub Hacktoberfest combined status (suggestion label override)',
namedParams: {
year: '2022',
year: '2021',
user: 'tmrowco',
repo: 'tmrowapp-contrib',
},
@@ -90,7 +90,7 @@ export default class GithubHacktoberfestCombinedStatus extends GithubAuthV4Servi
suggestion_label: 'help wanted',
},
staticPreview: this.render({
year: '2022',
year: '2021',
suggestedIssueCount: 12,
contributionCount: 8,
daysLeft: 15,

View File

@@ -15,29 +15,14 @@ const schema = Joi.array()
author: Joi.object({
date: Joi.string().required(),
}).required(),
committer: Joi.object({
date: Joi.string().required(),
}).required(),
}).required(),
}).required()
)
.required()
.min(1)
const queryParamSchema = Joi.object({
display_timestamp: Joi.string()
.valid('author', 'committer')
.default('author'),
}).required()
export default class GithubLastCommit extends GithubAuthV3Service {
static category = 'activity'
static route = {
base: 'github/last-commit',
pattern: ':user/:repo/:branch*',
queryParamSchema,
}
static route = { base: 'github/last-commit', pattern: ':user/:repo/:branch*' }
static examples = [
{
title: 'GitHub last commit',
@@ -60,17 +45,6 @@ export default class GithubLastCommit extends GithubAuthV3Service {
staticPreview: this.render({ commitDate: '2013-07-31T20:01:41Z' }),
...commonExampleAttrs,
},
{
title: 'GitHub last commit (by committer)',
pattern: ':user/:repo',
namedParams: {
user: 'google',
repo: 'skia',
},
queryParams: { display_timestamp: 'committer' },
staticPreview: this.render({ commitDate: '2022-10-15T20:01:41Z' }),
...commonExampleAttrs,
},
]
static defaultBadgeData = { label: 'last commit' }
@@ -91,11 +65,8 @@ export default class GithubLastCommit extends GithubAuthV3Service {
})
}
async handle({ user, repo, branch }, queryParams) {
async handle({ user, repo, branch }) {
const body = await this.fetch({ user, repo, branch })
return this.constructor.render({
commitDate: body[0].commit[queryParams.display_timestamp].date,
})
return this.constructor.render({ commitDate: body[0].commit.author.date })
}
}

View File

@@ -14,10 +14,6 @@ t.create('last commit (on branch)')
.get('/badges/badgr.co/shielded.json')
.expectBadge({ label: 'last commit', message: 'july 2013' })
t.create('last commit (by committer)')
.get('/badges/badgr.co/shielded.json?display_timestamp=committer')
.expectBadge({ label: 'last commit', message: 'july 2013' })
t.create('last commit (repo not found)')
.get('/badges/helmets.json')
.expectBadge({ label: 'last commit', message: 'repo not found' })

View File

@@ -2,7 +2,7 @@ import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('github pull request check state')
.get('/s/pulls/badges/shields/8486.json')
.get('/s/pulls/badges/shields/1110.json')
.expectBadge({ label: 'checks', message: 'failure' })
t.create('github pull request check state (pull request not found)')
@@ -10,7 +10,7 @@ t.create('github pull request check state (pull request not found)')
.expectBadge({ label: 'checks', message: 'pull request or repo not found' })
t.create(
"github pull request check state (ref returned by github doesn't exist)"
"github pull request check state (ref returned by github doesn't exist"
)
.get('/s/pulls/badges/shields/1110.json')
.intercept(
@@ -26,5 +26,5 @@ t.create(
})
t.create('github pull request check contexts')
.get('/contexts/pulls/badges/shields/8486.json')
.expectBadge({ label: 'checks', message: '2 success, 4 failure' })
.get('/contexts/pulls/badges/shields/1110.json')
.expectBadge({ label: 'checks', message: '1 failure' })

View File

@@ -8,30 +8,21 @@ import { documentation, errorMessagesFor } from './github-helpers.js'
const schema = Joi.alternatives(
Joi.object({
created_at: Joi.date().required(),
published_at: Joi.date().required(),
}).required(),
Joi.array()
.items(
Joi.object({
created_at: Joi.date().required(),
published_at: Joi.date().required(),
}).required()
)
.min(1)
)
const queryParamSchema = Joi.object({
display_date: Joi.string()
.valid('created_at', 'published_at')
.default('created_at'),
}).required()
export default class GithubReleaseDate extends GithubAuthV3Service {
static category = 'activity'
static route = {
base: 'github',
pattern: ':variant(release-date|release-date-pre)/:user/:repo',
queryParamSchema,
}
static examples = [
@@ -55,17 +46,6 @@ export default class GithubReleaseDate extends GithubAuthV3Service {
staticPreview: this.render({ date: '2017-04-13T07:50:27.000Z' }),
documentation,
},
{
title: 'GitHub Release Date - Published_At',
pattern: 'release-date/:user/:repo',
namedParams: {
user: 'microsoft',
repo: 'vscode',
},
queryParams: { display_date: 'published_at' },
staticPreview: this.render({ date: '2022-10-17T07:50:27.000Z' }),
documentation,
},
]
static defaultBadgeData = { label: 'release date' }
@@ -90,13 +70,11 @@ export default class GithubReleaseDate extends GithubAuthV3Service {
})
}
async handle({ variant, user, repo }, queryParams) {
async handle({ variant, user, repo }) {
const body = await this.fetch({ variant, user, repo })
if (Array.isArray(body)) {
return this.constructor.render({
date: body[0][queryParams.display_date],
})
return this.constructor.render({ date: body[0].created_at })
}
return this.constructor.render({ date: body[queryParams.display_date] })
return this.constructor.render({ date: body.created_at })
}
}

View File

@@ -9,27 +9,6 @@ t.create('Release Date. e.g release date|today')
message: isFormattedDate,
})
t.create('Release Date - display_date by `created_at` (default)')
.get('/release-date/microsoft/vscode.json?display_date=created_at')
.expectBadge({
label: 'release date',
message: isFormattedDate,
})
t.create('Release Date - display_date by `published_at`')
.get('/release-date/microsoft/vscode.json?display_date=published_at')
.expectBadge({
label: 'release date',
message: isFormattedDate,
})
t.create('Release Date - display_date by `published_at`, incorrect query param')
.get('/release-date/microsoft/vscode.json?display_date=published_attttttttt')
.expectBadge({
label: 'release date',
message: 'invalid query parameter: display_date',
})
t.create(
'Release Date - Should return `no releases or repo not found` for invalid repo'
)

View File

@@ -1,13 +0,0 @@
import { redirector } from '../index.js'
export default redirector({
category: 'coverage',
route: {
base: 'gitlab/coverage',
pattern: ':user/:repo/:branch',
},
transformPath: ({ user, repo }) =>
`/gitlab/pipeline-coverage/${user}/${repo}`,
transformQueryParams: ({ branch }) => ({ branch }),
dateAdded: new Date('2022-09-25'),
})

View File

@@ -1,22 +0,0 @@
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('Coverage redirect (with branch)')
.get('/gitlab-org/gitlab-runner/master.json')
.expectRedirect(
'/gitlab/pipeline-coverage/gitlab-org/gitlab-runner.json?branch=master'
)
t.create('Coverage redirect (with branch and job_name)')
.get('/gitlab-org/gitlab-runner/master.json?job_name=test coverage report')
.expectRedirect(
'/gitlab/pipeline-coverage/gitlab-org/gitlab-runner.json?branch=master&job_name=test%20coverage%20report'
)
t.create('Coverage redirect (with branch and gitlab_url)')
.get(
'/gitlab-org/gitlab-runner/master.json?gitlab_url=https://gitlab.gnome.org'
)
.expectRedirect(
'/gitlab/pipeline-coverage/gitlab-org/gitlab-runner.json?branch=master&gitlab_url=https%3a%2f%2fgitlab.gnome.org'
)

View File

@@ -13,7 +13,6 @@ const schema = Joi.object({
const queryParamSchema = Joi.object({
gitlab_url: optionalUrl,
job_name: Joi.string(),
branch: Joi.string(),
}).required()
const moreDocs = `
@@ -38,44 +37,50 @@ Also make sure you have set up code covrage parsing as described <a href="https:
</p>
`
export default class GitlabPipelineCoverage extends BaseSvgScrapingService {
export default class GitlabCoverage extends BaseSvgScrapingService {
static category = 'coverage'
static route = {
base: 'gitlab/pipeline-coverage',
pattern: ':project+',
base: 'gitlab/coverage',
pattern: ':user/:repo/:branch',
queryParamSchema,
}
static examples = [
{
title: 'Gitlab code coverage',
namedParams: { project: 'gitlab-org/gitlab-runner' },
queryParams: { branch: 'master' },
namedParams: {
user: 'gitlab-org',
repo: 'gitlab-runner',
branch: 'master',
},
staticPreview: this.render({ coverage: 67 }),
documentation: documentation + moreDocs,
},
{
title: 'Gitlab code coverage (specific job)',
namedParams: { project: 'gitlab-org/gitlab-runner' },
queryParams: { job_name: 'test coverage report', branch: 'master' },
namedParams: {
user: 'gitlab-org',
repo: 'gitlab-runner',
branch: 'master',
},
queryParams: { job_name: 'test coverage report' },
staticPreview: this.render({ coverage: 96 }),
documentation: documentation + moreDocs,
},
{
title: 'Gitlab code coverage (self-managed)',
namedParams: { project: 'GNOME/at-spi2-core' },
queryParams: { gitlab_url: 'https://gitlab.gnome.org', branch: 'master' },
namedParams: { user: 'GNOME', repo: 'at-spi2-core', branch: 'master' },
queryParams: { gitlab_url: 'https://gitlab.gnome.org' },
staticPreview: this.render({ coverage: 93 }),
documentation: documentation + moreDocs,
},
{
title: 'Gitlab code coverage (self-managed, specific job)',
namedParams: { project: 'GNOME/libhandy' },
namedParams: { user: 'GNOME', repo: 'libhandy', branch: 'master' },
queryParams: {
gitlab_url: 'https://gitlab.gnome.org',
job_name: 'unit-test',
branch: 'master',
},
staticPreview: this.render({ coverage: 93 }),
documentation: documentation + moreDocs,
@@ -91,13 +96,11 @@ export default class GitlabPipelineCoverage extends BaseSvgScrapingService {
}
}
async fetch({ project, baseUrl = 'https://gitlab.com', jobName, branch }) {
async fetch({ user, repo, branch, baseUrl = 'https://gitlab.com', jobName }) {
// Since the URL doesn't return a usable value when an invalid job name is specified,
// it is recommended to not use the query param at all if not required
jobName = jobName ? `?job=${jobName}` : ''
const url = `${baseUrl}/${decodeURIComponent(
project
)}/badges/${branch}/coverage.svg${jobName}`
const url = `${baseUrl}/${user}/${repo}/badges/${branch}/coverage.svg${jobName}`
const errorMessages = errorMessagesFor('project not found')
return this._requestSvg({
schema,
@@ -114,11 +117,12 @@ export default class GitlabPipelineCoverage extends BaseSvgScrapingService {
}
async handle(
{ project },
{ gitlab_url: baseUrl, job_name: jobName, branch }
{ user, repo, branch },
{ gitlab_url: baseUrl, job_name: jobName }
) {
const { message: coverage } = await this.fetch({
project,
user,
repo,
branch,
baseUrl,
jobName,

View File

@@ -3,28 +3,28 @@ import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('Coverage (branch)')
.get('/gitlab-org/gitlab-runner.json?branch=12-0-stable')
.get('/gitlab-org/gitlab-runner/12-0-stable.json')
.expectBadge({
label: 'coverage',
message: isIntegerPercentage,
})
t.create('Coverage (existent branch but coverage not set up)')
.get('/gitlab-org/gitlab-git-http-server.json?branch=master')
.get('/gitlab-org/gitlab-git-http-server/master.json')
.expectBadge({
label: 'coverage',
message: 'not set up',
})
t.create('Coverage (nonexistent branch)')
.get('/gitlab-org/gitlab-runner.json?branch=nope-not-a-branch')
.get('/gitlab-org/gitlab-runner/nope-not-a-branch.json')
.expectBadge({
label: 'coverage',
message: 'not set up',
})
t.create('Coverage (nonexistent repo)')
.get('/this-repo/does-not-exist.json')
.get('/this-repo/does-not-exist/neither-branch.json')
.expectBadge({
label: 'coverage',
message: 'inaccessible',
@@ -32,7 +32,7 @@ t.create('Coverage (nonexistent repo)')
t.create('Coverage (custom job)')
.get(
'/gitlab-org/gitlab-runner.json?branch=12-0-stable&job_name=test coverage report'
'/gitlab-org/gitlab-runner/12-0-stable.json?job_name=test coverage report'
)
.expectBadge({
label: 'coverage',
@@ -40,18 +40,14 @@ t.create('Coverage (custom job)')
})
t.create('Coverage (custom invalid job)')
.get(
'/gitlab-org/gitlab-runner.json?branch=12-0-stable&job_name=i dont exist'
)
.get('/gitlab-org/gitlab-runner/12-0-stable.json?job_name=i dont exist')
.expectBadge({
label: 'coverage',
message: 'not set up',
})
t.create('Coverage (custom gitlab URL)')
.get(
'/GNOME/at-spi2-core.json?gitlab_url=https://gitlab.gnome.org&branch=master'
)
.get('/GNOME/at-spi2-core/master.json?gitlab_url=https://gitlab.gnome.org')
.expectBadge({
label: 'coverage',
message: isIntegerPercentage,
@@ -59,7 +55,7 @@ t.create('Coverage (custom gitlab URL)')
t.create('Coverage (custom gitlab URL and job)')
.get(
'/GNOME/libhandy.json?gitlab_url=https://gitlab.gnome.org&branch=master&job_name=unit-test'
'/GNOME/libhandy/master.json?gitlab_url=https://gitlab.gnome.org&job_name=unit-test'
)
.expectBadge({
label: 'coverage',

View File

@@ -1,68 +0,0 @@
import Joi from 'joi'
import { optionalUrl } from '../validators.js'
import { metric } from '../text-formatters.js'
import { documentation, errorMessagesFor } from './gitlab-helper.js'
import GitLabBase from './gitlab-base.js'
/*
We're expecting a response like { "Ruby": 67.13, "JavaScript": 19.66 }
The keys could be anything and {} is a valid response (e.g: for an empty project)
*/
const schema = Joi.object().pattern(/./, Joi.number().min(0).max(100))
const queryParamSchema = Joi.object({
gitlab_url: optionalUrl,
}).required()
export default class GitlabLanguageCount extends GitLabBase {
static category = 'analysis'
static route = {
base: 'gitlab/languages/count',
pattern: ':project+',
queryParamSchema,
}
static examples = [
{
title: 'GitLab language count',
namedParams: {
project: 'gitlab-org/gitlab',
},
queryParams: { gitlab_url: 'https://gitlab.com' },
staticPreview: {
label: 'languages',
message: '5',
},
documentation,
},
]
static defaultBadgeData = { label: 'languages' }
static render({ languagesCount }) {
return {
message: metric(languagesCount),
color: 'blue',
}
}
async fetch({ project, baseUrl }) {
// https://docs.gitlab.com/ee/api/projects.html#languages
return super.fetch({
schema,
url: `${baseUrl}/api/v4/projects/${encodeURIComponent(
project
)}/languages`,
errorMessages: errorMessagesFor('project not found'),
})
}
async handle({ project }, { gitlab_url: baseUrl = 'https://gitlab.com' }) {
const data = await this.fetch({
project,
baseUrl,
})
return this.constructor.render({ languagesCount: Object.keys(data).length })
}
}

View File

@@ -1,25 +0,0 @@
import { isMetric } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('language count').get('/gitlab-org/gitlab.json').expectBadge({
label: 'languages',
message: isMetric,
color: 'blue',
})
t.create('language count (self-managed)')
.get('/gitlab-cn/gitlab.json?gitlab_url=https://jihulab.com')
.expectBadge({
label: 'languages',
message: isMetric,
color: 'blue',
})
t.create('language count (project not found)')
.get('/open/guoxudong.io/shields-test/do-not-exist.json')
.expectBadge({
label: 'languages',
message: 'project not found',
})

View File

@@ -1,79 +0,0 @@
import Joi from 'joi'
import { optionalUrl } from '../validators.js'
import { formatDate } from '../text-formatters.js'
import { age as ageColor } from '../color-formatters.js'
import { documentation, errorMessagesFor } from './gitlab-helper.js'
import GitLabBase from './gitlab-base.js'
const schema = Joi.array()
.items(
Joi.object({
committed_date: Joi.string().required(),
}).required()
)
.required()
.min(1)
const queryParamSchema = Joi.object({
ref: Joi.string(),
gitlab_url: optionalUrl,
}).required()
const refText = `
<p>
ref can be filled with the name of a branch, tag or revision range of the repository.
</p>
`
const defaultDocumentation = documentation + refText
export default class GitlabLastCommit extends GitLabBase {
static category = 'activity'
static route = {
base: 'gitlab/last-commit',
pattern: ':project+',
queryParamSchema,
}
static examples = [
{
title: 'GitLab last commit',
namedParams: {
project: 'gitlab-org/gitlab',
},
queryParams: { gitlab_url: 'https://gitlab.com' },
staticPreview: this.render({ commitDate: '2013-07-31T20:01:41Z' }),
documentation: defaultDocumentation,
},
]
static defaultBadgeData = { label: 'last commit' }
static render({ commitDate }) {
return {
message: formatDate(commitDate),
color: ageColor(Date.parse(commitDate)),
}
}
async fetch({ project, baseUrl, ref }) {
// https://docs.gitlab.com/ee/api/commits.html#list-repository-commits
return super.fetch({
url: `${baseUrl}/api/v4/projects/${encodeURIComponent(
project
)}/repository/commits`,
options: { searchParams: { ref_name: ref } },
schema,
errorMessages: errorMessagesFor('project not found'),
})
}
async handle(
{ project },
{ gitlab_url: baseUrl = 'https://gitlab.com', ref }
) {
const data = await this.fetch({ project, baseUrl, ref })
return this.constructor.render({ commitDate: data[0].committed_date })
}
}

View File

@@ -1,30 +0,0 @@
import { isFormattedDate } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('last commit (recent)').get('/gitlab-org/gitlab.json').expectBadge({
label: 'last commit',
message: isFormattedDate,
})
t.create('last commit (on ref and ancient)')
.get('/gitlab-org/gitlab.json?ref=v13.8.6-ee')
.expectBadge({
label: 'last commit',
message: 'march 2021',
})
t.create('last commit (self-managed)')
.get('/gitlab-cn/gitlab.json?gitlab_url=https://jihulab.com')
.expectBadge({
label: 'last commit',
message: isFormattedDate,
})
t.create('last commit (project not found)')
.get('/open/guoxudong.io/shields-test/do-not-exist.json')
.expectBadge({
label: 'last commit',
message: 'project not found',
})

View File

@@ -23,9 +23,9 @@ t.create('version installs | valid: numeric version')
})
t.create('version installs | valid: alphanumeric version')
.get('/build-failure-analyzer/1.17.2-DRE3.21.json')
.get('/build-failure-analyzer/1.17.2-DRE3.14.json')
.expectBadge({
label: 'installs@1.17.2-DRE3.21',
label: 'installs@1.17.2-DRE3.14',
message: isMetric,
})

View File

@@ -59,3 +59,10 @@ t.create('alerts: total alerts for a project with a github mapped host')
label: 'lgtm alerts',
message: Joi.string().regex(/^[0-9kM.]+$/),
})
t.create('alerts: total alerts for a project with a bitbucket mapped host')
.get('/bitbucket/atlassian/confluence-business-blueprints.json')
.expectBadge({
label: 'lgtm alerts',
message: Joi.string().regex(/^[0-9kM.]+$/),
})

View File

@@ -27,7 +27,7 @@ t.create('total downloads (valid)')
})
t.create('total downloads (tenant)')
.get('/vs-devcore.myget/vs-devcore/dt/MicroBuild.json')
.get('/cefsharp.myget/cefsharp/dt/CefSharp.Common.json')
.expectBadge({
label: 'downloads',
message: isMetric,

View File

@@ -1,11 +1,83 @@
import { deprecatedService } from '../index.js'
import Joi from 'joi'
import { starRating, metric } from '../text-formatters.js'
import { colorScale } from '../color-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService } from '../index.js'
export default deprecatedService({
category: 'rating',
route: {
const pkgReviewColor = colorScale([2, 3, 4])
const schema = Joi.object({
rating: Joi.number().min(0).max(1).precision(1).required().allow(null),
reviewsCount: nonNegativeInteger,
}).required()
// Repository for this service is: https://github.com/iqubex-technologies/pkgreview.dev
// Internally the service leverages the npms.io API (https://api.npms.io/v2)
export default class PkgreviewRating extends BaseJsonService {
static category = 'rating'
static route = {
base: 'pkgreview',
pattern: ':various*',
},
label: 'pkgreview',
dateAdded: new Date('2022-10-07'),
})
pattern: ':format(rating|stars)/:pkgManager(npm)/:pkgSlug+',
}
static examples = [
{
title: 'pkgreview.dev Package Ratings',
pattern: 'rating/:pkgManager/:pkgSlug+',
namedParams: { pkgManager: 'npm', pkgSlug: 'react' },
staticPreview: this.render({
format: 'rating',
rating: 3.5,
reviewsCount: 237,
}),
},
{
title: 'pkgreview.dev Star Ratings',
pattern: 'stars/:pkgManager/:pkgSlug+',
namedParams: { pkgManager: 'npm', pkgSlug: 'react' },
staticPreview: this.render({
format: 'stars',
rating: 1.5,
reviewsCount: 200,
}),
},
]
static render({ rating, reviewsCount, format }) {
const message =
format === 'rating'
? `${+parseFloat(rating).toFixed(1)}/5 (${metric(reviewsCount)})`
: starRating(rating)
return {
message,
label: format,
color: pkgReviewColor(rating),
}
}
async fetch({ pkgManager, pkgSlug }) {
return this._requestJson({
schema,
url: `https://pkgreview.now.sh/api/v1/${pkgManager}/${encodeURIComponent(
pkgSlug
)}`,
errorMessages: {
404: 'package not found',
},
})
}
async handle({ format, pkgManager, pkgSlug }) {
const { reviewsCount, rating } = await this.fetch({
pkgManager,
pkgSlug,
})
return this.constructor.render({
reviewsCount,
format,
rating: rating * 5,
})
}
}

View File

@@ -1,15 +1,23 @@
import { ServiceTester } from '../tester.js'
import { withRegex, isStarRating } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
export const t = new ServiceTester({
id: 'pkgreview',
title: 'PkgReview',
pathPrefix: '/pkgreview',
})
const isRatingWithReviews = withRegex(
/^(([0-4](.?([0-9]))?)|5)\/5?\s*\([0-9]*\)$/
)
t.create('Stars Badge')
t.create('Stars Badge renders')
.get('/stars/npm/react.json')
.expectBadge({ label: 'pkgreview', message: 'no longer available' })
.expectBadge({ label: 'stars', message: isStarRating })
t.create('Rating Badge')
t.create('Rating Badge renders')
.get('/rating/npm/react.json')
.expectBadge({ label: 'pkgreview', message: 'no longer available' })
.expectBadge({ label: 'rating', message: isRatingWithReviews })
t.create('nonexistent package')
.get('/rating/npm/ohlolweallknowthispackagewontexist.json')
.expectBadge({
label: 'rating',
message: 'package not found',
color: 'red',
})

View File

@@ -35,7 +35,7 @@ class PowershellGalleryPlatformSupport extends BaseXmlService {
static examples = [
{
title: 'PowerShell Gallery',
namedParams: { packageName: 'PackageManagement' },
namedParams: { packageName: 'DNS.1.1.1.1' },
staticPreview: this.render({
platforms: ['windows', 'macos', 'linux'],
}),

View File

@@ -47,7 +47,7 @@ t.create('version (legacy redirect: vpre)')
.get('/vpre/ACMESharp.svg')
.expectRedirect('/powershellgallery/v/ACMESharp.svg?include_prereleases')
t.create('platform (valid)').get('/p/PackageManagement.json').expectBadge({
t.create('platform (valid').get('/p/DNS.1.1.1.1.json').expectBadge({
label: 'platform',
message: isPlatform,
})

View File

@@ -26,7 +26,7 @@ export default class PubPoints extends BaseJsonService {
keywords,
documentation,
namedParams: { packageName: 'analysis_options' },
staticPreview: this.render({ grantedPoints: 120, maxPoints: 140 }),
staticPreview: this.render({ grantedPoints: 120, maxPoints: 130 }),
},
]

View File

@@ -7,7 +7,7 @@ t.create('pub points (valid)')
.get('/analysis_options.json')
.expectBadge({
label: 'points',
message: Joi.string().regex(/^\d+\/140$/),
message: Joi.string().regex(/^\d+\/130$/),
})
t.create('pub points (not found)').get('/analysisoptions.json').expectBadge({

View File

@@ -62,10 +62,7 @@ t.create('valid repo -- unregistered')
color: COLOR_MAP.unregistered,
})
t.create('invalid repo')
.timeout(15000)
.get('/github.com/repo/invalid-repo.json')
.expectBadge({
label: 'reuse',
message: 'Not a Git repository',
})
t.create('invalid repo').get('/github.com/repo/invalid-repo.json').expectBadge({
label: 'reuse',
message: 'Not a Git repository',
})

View File

@@ -8,7 +8,7 @@ const schema = Joi.object()
Joi.object({
level: Joi.string().required(),
message: Joi.string().required(),
})
}).required()
),
})
.required()

View File

@@ -155,15 +155,6 @@ const isCustomCompactTestTotals = makeCompactTestTotalsValidator({
skipped: '🤷',
})
const isOrdinalNumber = Joi.string().regex(/^[1-9][0-9]*(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ)$/)
const isOrdinalNumberDaily = Joi.string().regex(
/^[1-9][0-9]*(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ) daily$/
)
const isHumanized = Joi.string().regex(
/[0-9a-z]+ (second|seconds|minute|minutes|hour|hours|day|days|month|months|year|years)/
)
export {
isSemver,
isVPlusTripleDottedVersion,
@@ -196,7 +187,4 @@ export {
isCustomCompactTestTotals,
makeTestTotalsValidator,
makeCompactTestTotalsValidator,
isOrdinalNumber,
isOrdinalNumberDaily,
isHumanized,
}

View File

@@ -2,8 +2,8 @@ import Joi from 'joi'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService, NotFound } from '../index.js'
// https://help.testspace.com/reference/web-api#list-results
// case_counts|array|The contained cases [passed, failed, na, errored, untested]|counters of result
// https://help.testspace.com/docs/reference/web-api#list-results
// case_counts|array|The contained cases [passed, failed, na, errored]|counters of result
// session_* fields are for manual runs
// There are instances where the api returns a 200 status code with an empty array
// notably in cases where a space id is used
@@ -12,14 +12,14 @@ const schema = Joi.array()
Joi.object({
case_counts: Joi.array()
.items(nonNegativeInteger)
.min(5)
.max(5)
.min(4)
.max(4)
.required(),
})
)
.required()
// https://help.testspace.com/dashboard/overview#navigate
// https://help.testspace.com/docs/dashboard/overview-navigate
// Org is owner/account
// Project is generally a repository
// Space is a container, often a branch
@@ -49,11 +49,11 @@ export default class TestspaceBase extends BaseJsonService {
const [
{
case_counts: [passed, failed, skipped, errored, untested],
case_counts: [passed, failed, skipped, errored],
},
] = json
const total = passed + failed + skipped + errored + untested
const total = passed + failed + skipped + errored
return { passed, failed, skipped, errored, untested, total }
return { passed, failed, skipped, errored, total }
}
}

View File

@@ -5,7 +5,7 @@ export default class TestspaceTestCount extends TestspaceBase {
static route = {
base: 'testspace',
pattern:
':metric(total|passed|failed|skipped|errored|untested)/:org/:project/:space+',
':metric(total|passed|failed|skipped|errored)/:org/:project/:space+',
}
static examples = [
@@ -39,7 +39,7 @@ export default class TestspaceTestCount extends TestspaceBase {
}
transform({ json, metric }) {
const { passed, failed, skipped, errored, untested, total } =
const { passed, failed, skipped, errored, total } =
this.transformCaseCounts(json)
if (metric === 'total') {
return { value: total }
@@ -49,8 +49,6 @@ export default class TestspaceTestCount extends TestspaceBase {
return { value: failed }
} else if (metric === 'skipped') {
return { value: skipped }
} else if (metric === 'untested') {
return { value: untested }
} else {
return { value: errored }
}

View File

@@ -38,10 +38,5 @@ describe('TestspaceTestCount', function () {
message: '0',
color: 'informational',
})
given({ metric: 'untested', value: 0 }).expect({
label: 'untested tests',
message: '0',
color: 'informational',
})
})
})

View File

@@ -41,10 +41,3 @@ t.create('Errored')
label: 'errored tests',
message: isMetricAllowZero,
})
t.create('Untested')
.get('/untested/swellaby/swellaby:testspace-sample/main.json')
.expectBadge({
label: 'untested tests',
message: isMetricAllowZero,
})

View File

@@ -124,11 +124,9 @@ function formatDate(d) {
}
function formatRelativeDate(timestamp) {
const parsedDate = dayjs.unix(parseInt(timestamp, 10))
if (!parsedDate.isValid()) {
return 'invalid date'
}
return dayjs().to(parsedDate).toLowerCase()
return dayjs()
.to(dayjs.unix(parseInt(timestamp, 10)))
.toLowerCase()
}
export {

View File

@@ -153,11 +153,5 @@ describe('Text formatters', function () {
.describe('when given the beginning of october')
.expect('a month ago')
})
test(formatRelativeDate, () => {
given(9999999999999)
.describe('when given invalid date')
.expect('invalid date')
})
})
})

View File

@@ -21,12 +21,6 @@ const extensionQuerySchema = Joi.object({
.items(
Joi.object({
version: Joi.string().required(),
properties: Joi.array().items(
Joi.object({
key: Joi.string().required(),
value: Joi.any().required(),
})
),
})
)
.min(1)
@@ -73,12 +67,7 @@ export default class VisualStudioMarketplaceBase extends BaseJsonService {
criteria: [{ filterType: 7, value: extensionId }],
},
],
// Microsoft does not provide a clear API doc. It seems that the flag value is calculated
// as the combined hex values of the requested flags, converted to base 10.
// This was found using the vscode repo at:
// https://github.com/microsoft/vscode/blob/main/src/vs/platform/extensionManagement/common/extensionGalleryService.ts
// This flag value is 0x192.
flags: 402,
flags: 914,
}
const options = {
method: 'POST',

View File

@@ -1,18 +1,12 @@
import Joi from 'joi'
import { renderVersionBadge } from '../version.js'
import VisualStudioMarketplaceBase from './visual-studio-marketplace-base.js'
const queryParamSchema = Joi.object({
include_prereleases: Joi.equal(''),
}).required()
export default class VisualStudioMarketplaceVersion extends VisualStudioMarketplaceBase {
static category = 'version'
static route = {
base: '',
pattern: '(visual-studio-marketplace|vscode-marketplace)/v/:extensionId',
queryParamSchema,
}
static examples = [
@@ -23,14 +17,6 @@ export default class VisualStudioMarketplaceVersion extends VisualStudioMarketpl
staticPreview: this.render({ version: '0.2.7' }),
keywords: this.keywords,
},
{
title: 'Visual Studio Marketplace Version (including pre-releases)',
pattern: 'visual-studio-marketplace/v/:extensionId',
namedParams: { extensionId: 'swellaby.rust-pack' },
queryParams: { include_prereleases: null },
staticPreview: this.render({ version: '0.2.9-dev' }),
keywords: this.keywords,
},
]
static defaultBadgeData = {
@@ -41,33 +27,15 @@ export default class VisualStudioMarketplaceVersion extends VisualStudioMarketpl
return renderVersionBadge({ version })
}
transform({ json }, includePrereleases) {
transform({ json }) {
const { extension } = this.transformExtension({ json })
const preReleaseKey = 'Microsoft.VisualStudio.Code.PreRelease'
let version
if (!includePrereleases) {
version = extension.versions.find(
obj =>
!obj.properties.find(
({ key, value }) => key === preReleaseKey && value === 'true'
)
)?.version
}
// this condition acts as the 'else' clause AND as a fallback,
// in case all versions are pre-release
if (!version) {
version = extension.versions[0].version
}
const version = extension.versions[0].version
return { version }
}
async handle({ extensionId }, queryParams) {
async handle({ extensionId }) {
const json = await this.fetch({ extensionId })
const includePrereleases = queryParams.include_prereleases !== undefined
const { version } = this.transform({ json }, includePrereleases)
const { version } = this.transform({ json })
return this.constructor.render({ version })
}

View File

@@ -5,14 +5,14 @@ export const t = await createServiceTester()
const isMarketplaceVersion = withRegex(/^v(\d+\.\d+\.\d+)(\.\d+)?$/)
t.create('rating')
.get('/visual-studio-marketplace/v/lextudio.restructuredtext.json')
.get('/visual-studio-marketplace/v/ritwickdey.LiveServer.json')
.expectBadge({
label: 'version',
message: isMarketplaceVersion,
})
t.create('version')
.get('/visual-studio-marketplace/v/lextudio.restructuredtext.json')
.get('/visual-studio-marketplace/v/ritwickdey.LiveServer.json')
.intercept(nock =>
nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
.post('/extensionquery/')
@@ -23,27 +23,8 @@ t.create('version')
{
statistics: [],
versions: [
{
version: '1.3.8-alpha',
properties: [
{
key: 'Microsoft.VisualStudio.Services.Branding.Theme',
value: 'light',
},
{
key: 'Microsoft.VisualStudio.Code.PreRelease',
value: 'true',
},
],
},
{
version: '1.0.0',
properties: [
{
key: 'Microsoft.VisualStudio.Services.Branding.Theme',
value: 'light',
},
],
},
],
releaseDate: '2019-04-13T07:50:27.000Z',
@@ -60,144 +41,8 @@ t.create('version')
color: 'blue',
})
t.create(
'version - includePrereleases flag is false and response has pre-release only'
)
.get('/visual-studio-marketplace/v/lextudio.restructuredtext.json')
.intercept(nock =>
nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
.post('/extensionquery/')
.reply(200, {
results: [
{
extensions: [
{
statistics: [],
versions: [
{
version: '1.3.8',
properties: [
{
key: 'Microsoft.VisualStudio.Services.Branding.Theme',
value: 'light',
},
{
key: 'Microsoft.VisualStudio.Code.PreRelease',
value: 'true',
},
],
},
{
version: '1.3.7',
properties: [
{
key: 'Microsoft.VisualStudio.Services.Branding.Theme',
value: 'light',
},
{
key: 'Microsoft.VisualStudio.Code.PreRelease',
value: 'true',
},
],
},
{
version: '1.3.6',
properties: [
{
key: 'Microsoft.VisualStudio.Services.Branding.Theme',
value: 'light',
},
{
key: 'Microsoft.VisualStudio.Code.PreRelease',
value: 'true',
},
],
},
],
releaseDate: '2019-04-13T07:50:27.000Z',
lastUpdated: '2019-04-13T07:50:27.000Z',
},
],
},
],
})
)
.expectBadge({
label: 'version',
message: 'v1.3.8',
color: 'blue',
})
t.create('version - prerelease key has false value')
.get('/visual-studio-marketplace/v/lextudio.restructuredtext.json')
.intercept(nock =>
nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
.post('/extensionquery/')
.reply(200, {
results: [
{
extensions: [
{
statistics: [],
versions: [
{
version: '1.3.8',
properties: [
{
key: 'Microsoft.VisualStudio.Services.Branding.Theme',
value: 'light',
},
{
key: 'Microsoft.VisualStudio.Code.PreRelease',
value: 'true',
},
],
},
{
version: '1.3.7',
properties: [
{
key: 'Microsoft.VisualStudio.Services.Branding.Theme',
value: 'light',
},
{
key: 'Microsoft.VisualStudio.Code.PreRelease',
value: 'true',
},
],
},
{
version: '1.3.6',
properties: [
{
key: 'Microsoft.VisualStudio.Services.Branding.Theme',
value: 'light',
},
{
key: 'Microsoft.VisualStudio.Code.PreRelease',
value: 'false',
},
],
},
],
releaseDate: '2019-04-13T07:50:27.000Z',
lastUpdated: '2019-04-13T07:50:27.000Z',
},
],
},
],
})
)
.expectBadge({
label: 'version',
message: 'v1.3.6',
color: 'blue',
})
t.create('pre-release version')
.get(
'/visual-studio-marketplace/v/swellaby.vscode-rust-test-adapter.json?include_prereleases'
)
.get('/visual-studio-marketplace/v/swellaby.vscode-rust-test-adapter.json')
.intercept(nock =>
nock('https://marketplace.visualstudio.com/_apis/public/gallery/')
.post('/extensionquery/')
@@ -209,26 +54,7 @@ t.create('pre-release version')
statistics: [],
versions: [
{
version: '1.3.8-alpha',
properties: [
{
key: 'Microsoft.VisualStudio.Services.Branding.Theme',
value: 'light',
},
{
key: 'Microsoft.VisualStudio.Code.PreRelease',
value: 'true',
},
],
},
{
version: '1.0.0',
properties: [
{
key: 'Microsoft.VisualStudio.Services.Branding.Theme',
value: 'light',
},
],
version: '0.3.8',
},
],
releaseDate: '2019-04-13T07:50:27.000Z',
@@ -241,7 +67,7 @@ t.create('pre-release version')
)
.expectBadge({
label: 'version',
message: 'v1.3.8-alpha',
message: 'v0.3.8',
color: 'orange',
})

View File

@@ -1,126 +0,0 @@
import Joi from 'joi'
import dayjs from 'dayjs'
import calendar from 'dayjs/plugin/calendar.js'
import duration from 'dayjs/plugin/duration.js'
import relativeTime from 'dayjs/plugin/relativeTime.js'
import { BaseJsonService } from '../index.js'
import { metric as formatMetric, ordinalNumber } from '../text-formatters.js'
dayjs.extend(calendar)
dayjs.extend(duration)
dayjs.extend(relativeTime)
const schema = Joi.object({
Keys: Joi.alternatives(Joi.string(), Joi.number()).required(),
Clicks: Joi.alternatives(Joi.string(), Joi.number()).required(),
UptimeSeconds: Joi.alternatives(Joi.string(), Joi.number()).required(),
Download: Joi.string().required(),
Upload: Joi.string().required(),
Ranks: Joi.object({
Keys: Joi.string().required(),
Clicks: Joi.string().required(),
Download: Joi.string().required(),
Upload: Joi.string().required(),
Uptime: Joi.string().required(),
}),
}).required()
const queryParamSchema = Joi.object({
rank: Joi.equal(''),
}).required()
export default class WhatPulse extends BaseJsonService {
static category = 'activity'
static route = {
base: 'whatpulse',
pattern:
':metric(keys|clicks|uptime|download|upload)/:userType(user|team)/:id',
queryParamSchema,
}
static examples = [
{
title: 'WhatPulse user metric',
namedParams: { metric: 'keys', userType: 'user', id: '179734' },
staticPreview: this.render({
metric: 'keys',
metricValue: '21G',
}),
},
{
title: 'WhatPulse team metric - rank',
namedParams: {
metric: 'upload',
userType: 'team',
id: 'dutch power cows',
},
queryParams: { rank: null },
staticPreview: this.render({
metric: 'upload',
metricValue: '1ˢᵗ',
}),
},
]
static defaultBadgeData = { label: 'whatpulse' }
static render({ metric, metricValue }) {
return {
label: metric,
message: metricValue,
color: 'informational',
}
}
async fetch({ userType, id }) {
return await this._requestJson({
schema,
url: `https://api.whatpulse.org/${userType}.php?${userType}=${id}&format=json`,
})
}
toLowerKeys(obj) {
return Object.keys(obj).reduce((accumulator, key) => {
accumulator[key.toLowerCase()] = obj[key]
return accumulator
}, {})
}
transform({ json, metric }, { rank }) {
// We want to compare with lowercase keys from the WhatPulse's API.
const jsonLowercase = this.toLowerKeys(json)
jsonLowercase.ranks = this.toLowerKeys(json.Ranks)
// Just metric, no rank.
if (rank === undefined) {
if (metric === 'uptime') {
return dayjs.duration(jsonLowercase.uptimeseconds, 'seconds').humanize()
}
let metricValue
metricValue = jsonLowercase[metric]
if (metric === 'keys' || metric === 'clicks') {
metricValue = formatMetric(metricValue)
}
if (metric === 'upload' || metric === 'download') {
metricValue = metricValue.replace(/([A-Za-z]+)/, ' $1')
}
return metricValue
}
// Rank achieved by the user/team with the given metric.
const rankFromResp = jsonLowercase.ranks[metric]
return ordinalNumber(rankFromResp)
}
async handle({ metric, userType, id }, { rank }) {
const json = await this.fetch({ userType, id, metric })
const metricValue = this.transform({ json, metric }, { rank })
return this.constructor.render({ metric, metricValue })
}
}

View File

@@ -1,42 +0,0 @@
import { createServiceTester } from '../tester.js'
import {
isFileSize,
isHumanized,
isMetric,
isOrdinalNumber,
} from '../test-validators.js'
export const t = await createServiceTester()
t.create('WhatPulse user as user id, uptime')
.get('/uptime/user/179734.json')
.expectBadge({ label: 'uptime', message: isHumanized })
t.create('WhatPulse user as user name, keys')
.get('/keys/user/jerone.json')
.expectBadge({ label: 'keys', message: isMetric })
t.create('WhatPulse team as team id, clicks')
.get('/clicks/team/1295.json')
.expectBadge({ label: 'clicks', message: isMetric })
t.create('WhatPulse team as team id, download')
.get('/download/team/1295.json')
.expectBadge({ label: 'download', message: isFileSize })
t.create('WhatPulse team as team id, upload')
.get('/upload/team/1295.json')
.expectBadge({ label: 'upload', message: isFileSize })
t.create('WhatPulse team as team name, keys - from Ranks')
.get('/keys/team/dutch power cows.json?rank')
.expectBadge({ label: 'keys', message: isOrdinalNumber })
t.create(
'WhatPulse invalid metric name (not one of the options from the modal`s dropdown)'
)
.get('/UpTIMe/user/jerone.json')
.expectBadge({ label: '404', message: 'badge not found' })
t.create('WhatPulse incorrect user name')
.get('/uptime/user/NonExistentUsername.json')
.expectBadge({ label: 'whatpulse', message: 'invalid response data' })

View File

@@ -190,22 +190,6 @@ t.create('Plugin Required PHP Version')
t.create('Plugin Required PHP Version (Not Set)')
.get('/plugin/required-php/akismet.json')
.intercept(nock =>
nock('https://api.wordpress.org')
.get('/plugins/info/1.2/')
.query(mockedQuerySelector)
.reply(200, {
version: '1.2',
rating: 80,
num_ratings: 100,
downloaded: 100,
active_installs: 100,
requires: false,
tested: '4.0.0',
last_updated: '2020-01-01 7:21am GMT',
requires_php: false,
})
)
.expectBadge({
label: 'php',
message: 'not set for this plugin',