From 3dbe65561193dd471858592236cc1ad38d1c4a5d Mon Sep 17 00:00:00 2001 From: Pierre-Yves B Date: Wed, 17 Jul 2019 20:00:29 +0100 Subject: [PATCH] Display one decimal for metrics smaller than 10 (#3735) * Display one decimal for metrics smaller than 10 * Update test validators * Run Prettier * Update GitHub regexes --- services/discourse/discourse.tester.js | 16 ++++++++++++---- services/github/github-commits-since.tester.js | 9 +++++---- services/github/github-downloads.tester.js | 14 ++++++++++---- services/github/github-followers.tester.js | 4 ++-- services/github/github-issues.tester.js | 12 ++++++++---- services/github/github-stars.tester.js | 4 ++-- services/test-validators.js | 8 +++++--- services/text-formatters.js | 14 +++++++++++--- services/text-formatters.spec.js | 16 +++++++++++++--- services/wordpress/wordpress-downloads.tester.js | 5 ++--- 10 files changed, 70 insertions(+), 32 deletions(-) diff --git a/services/discourse/discourse.tester.js b/services/discourse/discourse.tester.js index b5b6ab4c55..8f3d114d54 100644 --- a/services/discourse/discourse.tester.js +++ b/services/discourse/discourse.tester.js @@ -97,28 +97,36 @@ t.create('Topics') .get('/https/meta.discourse.org/topics.json') .expectBadge({ label: 'discourse', - message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? topics$/), + message: Joi.string().regex( + /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) topics$/ + ), }) t.create('Posts') .get('/https/meta.discourse.org/posts.json') .expectBadge({ label: 'discourse', - message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? posts$/), + message: Joi.string().regex( + /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) posts$/ + ), }) t.create('Users') .get('/https/meta.discourse.org/users.json') .expectBadge({ label: 'discourse', - message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? users$/), + message: Joi.string().regex( + /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) users$/ + ), }) t.create('Likes') .get('/https/meta.discourse.org/likes.json') .expectBadge({ label: 'discourse', - message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? likes$/), + message: Joi.string().regex( + /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) likes$/ + ), }) t.create('Status') diff --git a/services/github/github-commits-since.tester.js b/services/github/github-commits-since.tester.js index 4bcc23d123..6a4be7122d 100644 --- a/services/github/github-commits-since.tester.js +++ b/services/github/github-commits-since.tester.js @@ -1,13 +1,14 @@ 'use strict' const Joi = require('@hapi/joi') +const { isMetric } = require('../test-validators') const t = (module.exports = require('../tester').createServiceTester()) t.create('Commits since') .get('/badges/shields/a0663d8da53fb712472c02665e6ff7547ba945b7.json') .expectBadge({ label: Joi.string().regex(/^(commits since){1}[\s\S]+$/), - message: Joi.string().regex(/^\w+$/), + message: isMetric, color: 'blue', }) @@ -15,21 +16,21 @@ t.create('Commits since (branch)') .get('/badges/shields/60be4859585650e8c2b87669e3a39d98ca084e98/gh-pages.json') .expectBadge({ label: Joi.string().regex(/^(commits since){1}[\s\S]+$/), - message: Joi.string().regex(/^\w+$/), + message: isMetric, }) t.create('Commits since by latest release') .get('/microsoft/typescript/latest.json') .expectBadge({ label: Joi.string().regex(/^(commits since){1}[\s\S]+$/), - message: Joi.string().regex(/^\d+\w?$/), + message: isMetric, }) t.create('Commits since by latest release (branch)') .get('/microsoft/typescript/latest/master.json') .expectBadge({ label: Joi.string().regex(/^(commits since){1}[\s\S]+$/), - message: Joi.string().regex(/^\d+\w?$/), + message: isMetric, }) t.create('Commits since (version not found)') diff --git a/services/github/github-downloads.tester.js b/services/github/github-downloads.tester.js index 9f741ad5f8..14c69ffac8 100644 --- a/services/github/github-downloads.tester.js +++ b/services/github/github-downloads.tester.js @@ -40,21 +40,27 @@ t.create('downloads for specific asset without slash') .get('/downloads/atom/atom/v0.190.0/atom-amd64.deb.json') .expectBadge({ label: 'downloads@v0.190.0', - message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? \[atom-amd64\.deb\]$/), + message: Joi.string().regex( + /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) \[atom-amd64\.deb\]$/ + ), }) t.create('downloads for specific asset from latest release') .get('/downloads/atom/atom/latest/atom-amd64.deb.json') .expectBadge({ label: 'downloads@latest', - message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? \[atom-amd64\.deb\]$/), + message: Joi.string().regex( + /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) \[atom-amd64\.deb\]$/ + ), }) t.create('downloads-pre for specific asset from latest release') .get('/downloads-pre/atom/atom/latest/atom-amd64.deb.json') .expectBadge({ label: 'downloads@latest', - message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? \[atom-amd64\.deb\]$/), + message: Joi.string().regex( + /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) \[atom-amd64\.deb\]$/ + ), }) t.create('downloads for release with slash') @@ -66,7 +72,7 @@ t.create('downloads for specific asset with slash') .expectBadge({ label: 'downloads@stable/v2.2.8', message: Joi.string().regex( - /^[0-9]+[kMGTPEZY]? \[dban-2\.2\.8_i586\.iso\]$/ + /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) \[dban-2\.2\.8_i586\.iso\]$/ ), }) diff --git a/services/github/github-followers.tester.js b/services/github/github-followers.tester.js index 852d29c79c..8ad5d8dbb2 100644 --- a/services/github/github-followers.tester.js +++ b/services/github/github-followers.tester.js @@ -1,13 +1,13 @@ 'use strict' -const Joi = require('@hapi/joi') +const { isMetric } = require('../test-validators') const t = (module.exports = require('../tester').createServiceTester()) t.create('Followers') .get('/webcaetano.json') .expectBadge({ label: 'followers', - message: Joi.string().regex(/^\w+$/), + message: isMetric, }) t.create('Followers (user not found)') diff --git a/services/github/github-issues.tester.js b/services/github/github-issues.tester.js index 556eb10cfc..5e88f6d124 100644 --- a/services/github/github-issues.tester.js +++ b/services/github/github-issues.tester.js @@ -8,14 +8,16 @@ t.create('GitHub closed pull requests') .get('/issues-pr-closed/badges/shields.json') .expectBadge({ label: 'pull requests', - message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? closed$/), + message: Joi.string().regex( + /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) closed$/ + ), }) t.create('GitHub closed pull requests raw') .get('/issues-pr-closed-raw/badges/shields.json') .expectBadge({ label: 'closed pull requests', - message: Joi.string().regex(/^\w+?$/), + message: isMetric, }) t.create('GitHub pull requests') @@ -36,14 +38,16 @@ t.create('GitHub closed issues') .get('/issues-closed/badges/shields.json') .expectBadge({ label: 'issues', - message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? closed$/), + message: Joi.string().regex( + /^([0-9]+[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) closed$/ + ), }) t.create('GitHub closed issues raw') .get('/issues-closed-raw/badges/shields.json') .expectBadge({ label: 'closed issues', - message: Joi.string().regex(/^\w+\+?$/), + message: isMetric, }) t.create('GitHub open issues') diff --git a/services/github/github-stars.tester.js b/services/github/github-stars.tester.js index 92ad07afef..96bb123407 100644 --- a/services/github/github-stars.tester.js +++ b/services/github/github-stars.tester.js @@ -1,13 +1,13 @@ 'use strict' -const Joi = require('@hapi/joi') +const { isMetric } = require('../test-validators') const t = (module.exports = require('../tester').createServiceTester()) t.create('Stars') .get('/badges/shields.json') .expectBadge({ label: 'stars', - message: Joi.string().regex(/^\w+$/), + message: isMetric, link: [ 'https://github.com/badges/shields', 'https://github.com/badges/shields/stargazers', diff --git a/services/test-validators.js b/services/test-validators.js index ed6519a41c..d25ecf87ce 100644 --- a/services/test-validators.js +++ b/services/test-validators.js @@ -62,12 +62,14 @@ const isStarRating = withRegex( ) // Required to be > 0, because accepting zero masks many problems. -const isMetric = withRegex(/^[1-9][0-9]*[kMGTPEZY]?$/) +const isMetric = withRegex(/^([1-9][0-9]*[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY])$/) -const isMetricOpenIssues = withRegex(/^[1-9][0-9]*[kMGTPEZY]? open$/) +const isMetricOpenIssues = withRegex( + /^([1-9][0-9]*[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) open$/ +) const isMetricOverTimePeriod = withRegex( - /^[1-9][0-9]*[kMGTPEZY]?\/(year|month|four weeks|week|day)$/ + /^([1-9][0-9]*[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY])\/(year|month|four weeks|week|day)$/ ) const isIntegerPercentage = withRegex(/^[1-9][0-9]?%|^100%|^0%$/) diff --git a/services/text-formatters.js b/services/text-formatters.js index c6768a1c3b..e932633009 100644 --- a/services/text-formatters.js +++ b/services/text-formatters.js @@ -56,9 +56,17 @@ function metric(n) { for (let i = metricPrefix.length - 1; i >= 0; i--) { const limit = metricPower[i] if (n >= limit) { - n = Math.round(n / limit) - if (n < 1000) { - return `${n}${metricPrefix[i]}` + const scaledN = n / limit + if (scaledN < 10) { + // For "small" numbers, display one decimal digit unless it is 0. + const oneDecimalN = scaledN.toFixed(1) + if (oneDecimalN.charAt(oneDecimalN.length - 1) !== '0') { + return `${oneDecimalN}${metricPrefix[i]}` + } + } + const roundedN = Math.round(scaledN) + if (roundedN < 1000) { + return `${roundedN}${metricPrefix[i]}` } else { return `1${metricPrefix[i + 1]}` } diff --git a/services/text-formatters.spec.js b/services/text-formatters.spec.js index a9e79f39ff..0326cfe87b 100644 --- a/services/text-formatters.spec.js +++ b/services/text-formatters.spec.js @@ -42,14 +42,24 @@ describe('Text formatters', function() { test(metric, () => { given(999).expect('999') given(1000).expect('1k') + given(1100).expect('1.1k') + given(10100).expect('10k') given(999499).expect('999k') given(999500).expect('1M') - given(1578896212).expect('2G') - given(80000000000000).expect('80T') + given(1100000).expect('1.1M') + given(1578896212).expect('1.6G') + given(20000000000).expect('20G') + given(15788962120).expect('16G') + given(9949999999999).expect('9.9T') + given(9950000000001).expect('10T') given(4000000000000001).expect('4P') + given(4200000000000001).expect('4.2P') + given(7100700010058000200).expect('7.1E') given(71007000100580002000).expect('71E') given(1000000000000000000000).expect('1Z') - given(2222222222222222222222222).expect('2Y') + given(1100000000000000000000).expect('1.1Z') + given(2222222222222222222222222).expect('2.2Y') + given(22222222222222222222222222).expect('22Y') }) test(omitv, () => { diff --git a/services/wordpress/wordpress-downloads.tester.js b/services/wordpress/wordpress-downloads.tester.js index 45d5c62054..1bdc79b20b 100644 --- a/services/wordpress/wordpress-downloads.tester.js +++ b/services/wordpress/wordpress-downloads.tester.js @@ -1,6 +1,5 @@ 'use strict' -const Joi = require('@hapi/joi') const { ServiceTester } = require('../tester') const { isMetric, isMetricOverTimePeriod } = require('../test-validators') @@ -20,7 +19,7 @@ t.create('Plugin Downloads - Active') .get('/plugin/installs/akismet.json') .expectBadge({ label: 'active installs', - message: Joi.string().regex(/^[1-9][0-9]*[kMGTPEZY]\+?$/), + message: isMetric, }) t.create('Plugin Downloads - Day') @@ -54,7 +53,7 @@ t.create('Theme Downloads - Active') .get('/theme/installs/twentyseventeen.json') .expectBadge({ label: 'active installs', - message: Joi.string().regex(/^[1-9][0-9]*[kMGTPEZY]\+?$/), + message: isMetric, }) t.create('Plugin Downloads - Total | Not Found')