From 0026c210111bc6ad3b92d4fe35e8f08189822140 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Thu, 29 Aug 2024 14:03:54 +0100 Subject: [PATCH] fix: wildcard domain check (#24671) --- .../authorizedUrlListLogic.ts | 2 +- frontend/src/lib/utils.test.ts | 1372 +++++++++-------- frontend/src/lib/utils.tsx | 2 +- 3 files changed, 690 insertions(+), 686 deletions(-) diff --git a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts index c9d6bb6e02730..20d0811578258 100644 --- a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts +++ b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts @@ -52,7 +52,7 @@ export const validateProposedUrl = ( currentUrls: string[], onlyAllowDomains: boolean = false ): string | undefined => { - if (!onlyAllowDomains && !isURL(proposedUrl)) { + if (!isURL(proposedUrl)) { return 'Please enter a valid URL' } diff --git a/frontend/src/lib/utils.test.ts b/frontend/src/lib/utils.test.ts index 007acad06d36d..b4f4f54ea71d1 100644 --- a/frontend/src/lib/utils.test.ts +++ b/frontend/src/lib/utils.test.ts @@ -50,798 +50,802 @@ import { toParams, } from './utils' -describe('toParams', () => { - it('handles unusual input', () => { - expect(toParams({})).toEqual('') - expect(toParams([])).toEqual('') - expect(toParams(undefined as any)).toEqual('') - expect(toParams(null as any)).toEqual('') - }) - - it('is tolerant of empty objects', () => { - const left = toParams({ a: 'b', ...{}, b: 'c' }) - const right = toParams({ a: 'b', ...{}, ...{}, b: 'c' }) - expect(left).toEqual(right) - }) - - it('can handle numeric values', () => { - const actual = toParams({ a: 123 }) - expect(actual).toEqual('a=123') - }) - - it('encodes arrays as a single query param', () => { - const actual = toParams({ include: ['a', 'b'] }) - expect(actual).toEqual('include=%5B%22a%22%2C%22b%22%5D') - }) +describe('lib/utils', () => { + describe('toParams', () => { + it('handles unusual input', () => { + expect(toParams({})).toEqual('') + expect(toParams([])).toEqual('') + expect(toParams(undefined as any)).toEqual('') + expect(toParams(null as any)).toEqual('') + }) - it('can explode arrays to individual parameters', () => { - const actual = toParams({ include: ['a', 'b'] }, true) - expect(actual).toEqual('include=a&include=b') - }) -}) + it('is tolerant of empty objects', () => { + const left = toParams({ a: 'b', ...{}, b: 'c' }) + const right = toParams({ a: 'b', ...{}, ...{}, b: 'c' }) + expect(left).toEqual(right) + }) -describe('capitalizeFirstLetter()', () => { - it('returns the capitalized string', () => { - expect(capitalizeFirstLetter('jane')).toEqual('Jane') - expect(capitalizeFirstLetter('hello there!')).toEqual('Hello there!') - expect(capitalizeFirstLetter('underscores_make_no_difference')).toEqual('Underscores_make_no_difference') - }) -}) + it('can handle numeric values', () => { + const actual = toParams({ a: 123 }) + expect(actual).toEqual('a=123') + }) -describe('identifierToHuman()', () => { - it('humanizes properly', () => { - expect(identifierToHuman('testIdentifier')).toEqual('Test identifier') - expect(identifierToHuman('testIdentifierX')).toEqual('Test identifier x') - expect(identifierToHuman('something ')).toEqual('Something') - expect(identifierToHuman(' some_property')).toEqual('Some property') - expect(identifierToHuman(' Number666')).toEqual('Number 666') - expect(identifierToHuman('7x')).toEqual('7x') - expect(identifierToHuman('7X')).toEqual('7 x') - expect(identifierToHuman('500')).toEqual('500') - expect(identifierToHuman(404)).toEqual('404') - expect(identifierToHuman('CreateProject')).toEqual('Create project') - }) -}) + it('encodes arrays as a single query param', () => { + const actual = toParams({ include: ['a', 'b'] }) + expect(actual).toEqual('include=%5B%22a%22%2C%22b%22%5D') + }) -describe('midEllipsis()', () => { - it('returns same string if short', () => { - expect(midEllipsis('12', 10)).toEqual('12') - expect(midEllipsis('1234567890', 10)).toEqual('1234567890') + it('can explode arrays to individual parameters', () => { + const actual = toParams({ include: ['a', 'b'] }, true) + expect(actual).toEqual('include=a&include=b') + }) }) - it('formats string properly', () => { - expect(midEllipsis('1234567890', 2)).toEqual('1…') - expect(midEllipsis('1234567890', 4)).toEqual('12…0') - expect(midEllipsis('1234567890', 8)).toEqual('1234…890') - expect(midEllipsis('1234567890', 9)).toEqual('1234…7890') - expect(midEllipsis('ZgZbZgD9Z4U2FsohDYAJ-hMdoxY7-oSdWwrEWtdBeM', 26)).toEqual('ZgZbZgD9Z4U2F…SdWwrEWtdBeM') - expect(midEllipsis('ZgZbZgD9Z4U2FsohDYAJ-hMdoxY7-oSdWwrEWtdBeM', 25)).toEqual('ZgZbZgD9Z4U2…SdWwrEWtdBeM') - expect(midEllipsis('ZgZbZgD9Z4U2FsohDYAJ-hMdoxY7-oSdWwrEWtdBeM', 24)).toEqual('ZgZbZgD9Z4U2…dWwrEWtdBeM') + describe('capitalizeFirstLetter()', () => { + it('returns the capitalized string', () => { + expect(capitalizeFirstLetter('jane')).toEqual('Jane') + expect(capitalizeFirstLetter('hello there!')).toEqual('Hello there!') + expect(capitalizeFirstLetter('underscores_make_no_difference')).toEqual('Underscores_make_no_difference') + }) }) -}) -describe('isURL()', () => { - it('recognizes URLs properly', () => { - expect(isURL('https://www.posthog.com')).toEqual(true) - expect(isURL('http://www.posthog.com')).toEqual(true) - expect(isURL('http://www.posthog.com:8000/images')).toEqual(true) - expect(isURL('http://localhost:8000/login?next=/insights')).toEqual(true) - expect(isURL('http://localhost:8000/activity/explore?properties=%5B%5D')).toEqual(true) - expect(isURL('https://apple.com/')).toEqual(true) - expect(isURL('https://stripe.com')).toEqual(true) - expect(isURL('https://spotify.com')).toEqual(true) - expect(isURL('https://sevenapp.events/')).toEqual(true) - expect(isURL('https://seven-stagingenv.web.app/')).toEqual(true) - expect(isURL('https://salesforce.co.uk/')).toEqual(true) - expect(isURL('https://valid.*.example.com')).toEqual(true) - expect(isURL('https://*.valid.com')).toEqual(true) - }) - - it('recognizes non-URLs properly', () => { - expect(isURL('1234567890')).toEqual(false) - expect(isURL('www.posthog')).toEqual(false) - expect(isURL('-.posthog')).toEqual(false) - expect(isURL('posthog.3')).toEqual(false) - expect(isURL(1)).toEqual(false) - expect(isURL(true)).toEqual(false) - expect(isURL(null)).toEqual(false) - expect( - isURL( - 'https://client.rrrr.alpha.dev.foo.bar/9RvDy6gCmic_srrKs1db?sourceOrigin=rrrr&embedded={%22hostContext%22:%22landing%22,%22hostType%22:%22web%22,%22type%22:%22popsync%22}&share=1&wrapperUrl=https%3A%2F%2Fuat.rrrr.io%2F9RvDy6gCmicxyz&save=1&initialSearch={%22sites%22:%22google.com,gettyimages.com%22,%22safe%22:true,%22q%22:%22Perro%22}&opcid=4360f861-ffff-4444-9999-5257065a7dc3&waitForToken=1' - ) - ).toEqual(false) + describe('identifierToHuman()', () => { + it('humanizes properly', () => { + expect(identifierToHuman('testIdentifier')).toEqual('Test identifier') + expect(identifierToHuman('testIdentifierX')).toEqual('Test identifier x') + expect(identifierToHuman('something ')).toEqual('Something') + expect(identifierToHuman(' some_property')).toEqual('Some property') + expect(identifierToHuman(' Number666')).toEqual('Number 666') + expect(identifierToHuman('7x')).toEqual('7x') + expect(identifierToHuman('7X')).toEqual('7 x') + expect(identifierToHuman('500')).toEqual('500') + expect(identifierToHuman(404)).toEqual('404') + expect(identifierToHuman('CreateProject')).toEqual('Create project') + }) }) -}) -describe('isExternalLink()', () => { - it('recognizes external links properly', () => { - expect(isExternalLink('http://www.posthog.com')).toEqual(true) - expect(isExternalLink('https://www.posthog.com')).toEqual(true) - expect(isExternalLink('mailto:ben@posthog.com')).toEqual(true) - }) + describe('midEllipsis()', () => { + it('returns same string if short', () => { + expect(midEllipsis('12', 10)).toEqual('12') + expect(midEllipsis('1234567890', 10)).toEqual('1234567890') + }) - it('recognizes non-external links properly', () => { - expect(isExternalLink('path')).toEqual(false) - expect(isExternalLink('/path')).toEqual(false) - expect(isExternalLink(1)).toEqual(false) - expect(isExternalLink(true)).toEqual(false) - expect(isExternalLink(null)).toEqual(false) + it('formats string properly', () => { + expect(midEllipsis('1234567890', 2)).toEqual('1…') + expect(midEllipsis('1234567890', 4)).toEqual('12…0') + expect(midEllipsis('1234567890', 8)).toEqual('1234…890') + expect(midEllipsis('1234567890', 9)).toEqual('1234…7890') + expect(midEllipsis('ZgZbZgD9Z4U2FsohDYAJ-hMdoxY7-oSdWwrEWtdBeM', 26)).toEqual('ZgZbZgD9Z4U2F…SdWwrEWtdBeM') + expect(midEllipsis('ZgZbZgD9Z4U2FsohDYAJ-hMdoxY7-oSdWwrEWtdBeM', 25)).toEqual('ZgZbZgD9Z4U2…SdWwrEWtdBeM') + expect(midEllipsis('ZgZbZgD9Z4U2FsohDYAJ-hMdoxY7-oSdWwrEWtdBeM', 24)).toEqual('ZgZbZgD9Z4U2…dWwrEWtdBeM') + }) }) -}) -describe('compactNumber()', () => { - it('formats number correctly', () => { - expect(compactNumber(10)).toEqual('10') - expect(compactNumber(293)).toEqual('293') - expect(compactNumber(5001)).toEqual('5 K') - expect(compactNumber(5312)).toEqual('5.31 K') - expect(compactNumber(5392)).toEqual('5.39 K') - expect(compactNumber(2833102)).toEqual('2.83 M') - expect(compactNumber(8283310234)).toEqual('8.28 B') - expect(compactNumber(null)).toEqual('-') - }) -}) + describe('isURL()', () => { + it('recognizes URLs properly', () => { + expect(isURL('https://www.posthog.com')).toEqual(true) + expect(isURL('http://www.posthog.com')).toEqual(true) + expect(isURL('http://www.posthog.com:8000/images')).toEqual(true) + expect(isURL('http://localhost:8000/login?next=/insights')).toEqual(true) + expect(isURL('http://localhost:8000/activity/explore?properties=%5B%5D')).toEqual(true) + expect(isURL('https://apple.com/')).toEqual(true) + expect(isURL('https://stripe.com')).toEqual(true) + expect(isURL('https://spotify.com')).toEqual(true) + expect(isURL('https://sevenapp.events/')).toEqual(true) + expect(isURL('https://seven-stagingenv.web.app/')).toEqual(true) + expect(isURL('https://salesforce.co.uk/')).toEqual(true) + expect(isURL('https://valid.*.example.com')).toEqual(true) + expect(isURL('https://*.valid.com')).toEqual(true) + }) -describe('roundToDecimal()', () => { - it('formats number correctly', () => { - expect(roundToDecimal(null)).toEqual('-') - expect(roundToDecimal(293)).toEqual('293.00') - expect(roundToDecimal(102.121233)).toEqual('102.12') - expect(roundToDecimal(102.99999)).toEqual('103.00') - expect(roundToDecimal(1212)).toEqual('1212.00') - expect(roundToDecimal(1212, 3)).toEqual('1212.000') + it('recognizes non-URLs properly', () => { + expect(isURL('1234567890')).toEqual(false) + expect(isURL('www.posthog')).toEqual(false) + expect(isURL('-.posthog')).toEqual(false) + expect(isURL('posthog.3')).toEqual(false) + expect(isURL(1)).toEqual(false) + expect(isURL(true)).toEqual(false) + expect(isURL(null)).toEqual(false) + expect(isURL('')).toEqual(false) + expect(isURL(' ')).toEqual(false) + expect( + isURL( + 'https://client.rrrr.alpha.dev.foo.bar/9RvDy6gCmic_srrKs1db?sourceOrigin=rrrr&embedded={%22hostContext%22:%22landing%22,%22hostType%22:%22web%22,%22type%22:%22popsync%22}&share=1&wrapperUrl=https%3A%2F%2Fuat.rrrr.io%2F9RvDy6gCmicxyz&save=1&initialSearch={%22sites%22:%22google.com,gettyimages.com%22,%22safe%22:true,%22q%22:%22Perro%22}&opcid=4360f861-ffff-4444-9999-5257065a7dc3&waitForToken=1' + ) + ).toEqual(false) + }) }) -}) -describe('pluralize()', () => { - it('handles singular cases', () => { - expect(pluralize(1, 'member')).toEqual('1 member') - expect(pluralize(1, 'bacterium', 'bacteria', true)).toEqual('1 bacterium') - expect(pluralize(1, 'word', undefined, false)).toEqual('word') - }) - it('handles plural cases', () => { - expect(pluralize(28321, 'member')).toEqual('28,321 members') - expect(pluralize(99, 'bacterium', 'bacteria')).toEqual('99 bacteria') - expect(pluralize(3, 'word', undefined, false)).toEqual('words') - }) -}) + describe('isExternalLink()', () => { + it('recognizes external links properly', () => { + expect(isExternalLink('http://www.posthog.com')).toEqual(true) + expect(isExternalLink('https://www.posthog.com')).toEqual(true) + expect(isExternalLink('mailto:ben@posthog.com')).toEqual(true) + }) -describe('endWithPunctation()', () => { - it('adds period at the end when needed', () => { - expect(endWithPunctation('Hello')).toEqual('Hello.') - expect(endWithPunctation('Learn more! ')).toEqual('Learn more!') - expect(endWithPunctation('Stop.')).toEqual('Stop.') - expect(endWithPunctation(null)).toEqual('') - expect(endWithPunctation(' ')).toEqual('') - expect(endWithPunctation(' Why? ')).toEqual('Why?') + it('recognizes non-external links properly', () => { + expect(isExternalLink('path')).toEqual(false) + expect(isExternalLink('/path')).toEqual(false) + expect(isExternalLink(1)).toEqual(false) + expect(isExternalLink(true)).toEqual(false) + expect(isExternalLink(null)).toEqual(false) + }) }) -}) -describe('getFormattedLastWeekDate()', () => { - it('happy case', () => { - tk.freeze(new Date(1330688329321)) - expect(getFormattedLastWeekDate()).toEqual('January 13 - March 2, 2012') - tk.reset() + describe('compactNumber()', () => { + it('formats number correctly', () => { + expect(compactNumber(10)).toEqual('10') + expect(compactNumber(293)).toEqual('293') + expect(compactNumber(5001)).toEqual('5 K') + expect(compactNumber(5312)).toEqual('5.31 K') + expect(compactNumber(5392)).toEqual('5.39 K') + expect(compactNumber(2833102)).toEqual('2.83 M') + expect(compactNumber(8283310234)).toEqual('8.28 B') + expect(compactNumber(null)).toEqual('-') + }) }) -}) -describe('dateFilterToText()', () => { - describe('not formatted', () => { - it('handles dayjs dates', () => { - const from = dayjs('2018-04-04T16:00:00.000Z') - const to = dayjs('2018-04-09T15:05:00.000Z') - - expect(dateFilterToText(from, to, 'custom')).toEqual('April 4 - April 9, 2018') + describe('roundToDecimal()', () => { + it('formats number correctly', () => { + expect(roundToDecimal(null)).toEqual('-') + expect(roundToDecimal(293)).toEqual('293.00') + expect(roundToDecimal(102.121233)).toEqual('102.12') + expect(roundToDecimal(102.99999)).toEqual('103.00') + expect(roundToDecimal(1212)).toEqual('1212.00') + expect(roundToDecimal(1212, 3)).toEqual('1212.000') }) + }) - it('handles various ranges', () => { - expect(dateFilterToText('dStart', null, 'default')).toEqual('Today') - expect(dateFilterToText('2020-01-02', '2020-01-05', 'default')).toEqual('2020-01-02 - 2020-01-05') - expect(dateFilterToText(null, null, 'default')).toEqual('default') - expect(dateFilterToText('-24h', null, 'default')).toEqual('Last 24 hours') - expect(dateFilterToText('-48h', undefined, 'default')).toEqual('Last 48 hours') - expect(dateFilterToText('-1d', null, 'default')).toEqual('Last 1 day') - expect(dateFilterToText('-1dStart', '-1dEnd', 'default')).toEqual('Yesterday') - expect(dateFilterToText('-1mStart', '-1mEnd', 'default')).toEqual('Previous month') + describe('pluralize()', () => { + it('handles singular cases', () => { + expect(pluralize(1, 'member')).toEqual('1 member') + expect(pluralize(1, 'bacterium', 'bacteria', true)).toEqual('1 bacterium') + expect(pluralize(1, 'word', undefined, false)).toEqual('word') }) - - it('can have overridden date options', () => { - expect(dateFilterToText('-21d', null, 'default', [{ key: 'Last 3 weeks', values: ['-21d'] }])).toEqual( - 'Last 3 weeks' - ) + it('handles plural cases', () => { + expect(pluralize(28321, 'member')).toEqual('28,321 members') + expect(pluralize(99, 'bacterium', 'bacteria')).toEqual('99 bacteria') + expect(pluralize(3, 'word', undefined, false)).toEqual('words') }) }) - describe('formatted', () => { - it('handles dayjs dates', () => { - const from = dayjs('2018-04-04T16:00:00.000Z') - const to = dayjs('2018-04-09T15:05:00.000Z') - - expect(dateFilterToText(from, to, 'custom', dateMapping, true)).toEqual('April 4 - April 9, 2018') + describe('endWithPunctation()', () => { + it('adds period at the end when needed', () => { + expect(endWithPunctation('Hello')).toEqual('Hello.') + expect(endWithPunctation('Learn more! ')).toEqual('Learn more!') + expect(endWithPunctation('Stop.')).toEqual('Stop.') + expect(endWithPunctation(null)).toEqual('') + expect(endWithPunctation(' ')).toEqual('') + expect(endWithPunctation(' Why? ')).toEqual('Why?') }) + }) - it('handles various ranges', () => { - // 2012-03-02T11:38:49.321Z + describe('getFormattedLastWeekDate()', () => { + it('happy case', () => { tk.freeze(new Date(1330688329321)) - expect(dateFilterToText('dStart', null, 'default', dateMapping, true)).toEqual('March 2, 2012') - expect(dateFilterToText('2020-01-02', '2020-01-05', 'default', dateMapping, true)).toEqual( - 'January 2 - January 5, 2020' - ) - expect(dateFilterToText(null, null, 'default', dateMapping, true)).toEqual('default') - expect(dateFilterToText('-24h', null, 'default', dateMapping, true)).toEqual('March 1 - March 2, 2012') - expect(dateFilterToText('-48h', undefined, 'default', dateMapping, true)).toEqual( - 'February 29 - March 2, 2012' - ) - expect(dateFilterToText('-1d', null, 'default', dateMapping, true)).toEqual('March 1 - March 2, 2012') - expect(dateFilterToText('-1dStart', '-1dEnd', 'default', dateMapping, true)).toEqual('March 1, 2012') - expect(dateFilterToText('-1mStart', '-1mEnd', 'default', dateMapping, true)).toEqual( - 'February 1 - February 29, 2012' - ) - expect(dateFilterToText('-180d', null, 'default', dateMapping, true)).toEqual( - 'September 4, 2011 - March 2, 2012' - ) + expect(getFormattedLastWeekDate()).toEqual('January 13 - March 2, 2012') tk.reset() }) + }) - it('can have overridden date options', () => { - tk.freeze(new Date(1330688329321)) - expect( - dateFilterToText( - '-21d', - null, - 'default', - [{ key: 'Last 3 weeks', values: ['-21d'], getFormattedDate: () => 'custom formatted date' }], - true - ) - ).toEqual('custom formatted date') - tk.reset() - }) + describe('dateFilterToText()', () => { + describe('not formatted', () => { + it('handles dayjs dates', () => { + const from = dayjs('2018-04-04T16:00:00.000Z') + const to = dayjs('2018-04-09T15:05:00.000Z') - it('can have overridden date format', () => { - const from = dayjs('2018-04-04T16:00:00.000Z').tz('America/New_York') - const to = dayjs('2018-04-09T15:05:00.000Z').tz('America/New_York') + expect(dateFilterToText(from, to, 'custom')).toEqual('April 4 - April 9, 2018') + }) - expect(dateFilterToText(from, to, 'custom', dateMapping, true, 'YYYY-MM-DD hh:mm:ss')).toEqual( - '2018-04-04 12:00:00 - 2018-04-09 11:05:00' - ) + it('handles various ranges', () => { + expect(dateFilterToText('dStart', null, 'default')).toEqual('Today') + expect(dateFilterToText('2020-01-02', '2020-01-05', 'default')).toEqual('2020-01-02 - 2020-01-05') + expect(dateFilterToText(null, null, 'default')).toEqual('default') + expect(dateFilterToText('-24h', null, 'default')).toEqual('Last 24 hours') + expect(dateFilterToText('-48h', undefined, 'default')).toEqual('Last 48 hours') + expect(dateFilterToText('-1d', null, 'default')).toEqual('Last 1 day') + expect(dateFilterToText('-1dStart', '-1dEnd', 'default')).toEqual('Yesterday') + expect(dateFilterToText('-1mStart', '-1mEnd', 'default')).toEqual('Previous month') + }) + + it('can have overridden date options', () => { + expect(dateFilterToText('-21d', null, 'default', [{ key: 'Last 3 weeks', values: ['-21d'] }])).toEqual( + 'Last 3 weeks' + ) + }) }) - }) -}) -describe('dateStringToDayJs', () => { - beforeEach(() => { - tk.freeze(1330688329321) // randomly chosen time on the 22nd of February 2022 - }) - afterEach(() => { - tk.reset() - }) + describe('formatted', () => { + it('handles dayjs dates', () => { + const from = dayjs('2018-04-04T16:00:00.000Z') + const to = dayjs('2018-04-09T15:05:00.000Z') - it('handles various dates', () => { - expect(dateStringToDayJs('2022-02-22')?.utc(true).toISOString()).toEqual('2022-02-22T00:00:00.000Z') - expect(dateStringToDayJs('1999-12-31')?.utc(true).toISOString()).toEqual('1999-12-31T00:00:00.000Z') - }) + expect(dateFilterToText(from, to, 'custom', dateMapping, true)).toEqual('April 4 - April 9, 2018') + }) - it('handles various units', () => { - expect(dateStringToDayJs('d')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') - expect(dateStringToDayJs('m')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') - expect(dateStringToDayJs('w')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') - expect(dateStringToDayJs('q')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') - expect(dateStringToDayJs('y')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') - expect(dateStringToDayJs('x')).toEqual(null) - }) + it('handles various ranges', () => { + // 2012-03-02T11:38:49.321Z + tk.freeze(new Date(1330688329321)) + expect(dateFilterToText('dStart', null, 'default', dateMapping, true)).toEqual('March 2, 2012') + expect(dateFilterToText('2020-01-02', '2020-01-05', 'default', dateMapping, true)).toEqual( + 'January 2 - January 5, 2020' + ) + expect(dateFilterToText(null, null, 'default', dateMapping, true)).toEqual('default') + expect(dateFilterToText('-24h', null, 'default', dateMapping, true)).toEqual('March 1 - March 2, 2012') + expect(dateFilterToText('-48h', undefined, 'default', dateMapping, true)).toEqual( + 'February 29 - March 2, 2012' + ) + expect(dateFilterToText('-1d', null, 'default', dateMapping, true)).toEqual('March 1 - March 2, 2012') + expect(dateFilterToText('-1dStart', '-1dEnd', 'default', dateMapping, true)).toEqual('March 1, 2012') + expect(dateFilterToText('-1mStart', '-1mEnd', 'default', dateMapping, true)).toEqual( + 'February 1 - February 29, 2012' + ) + expect(dateFilterToText('-180d', null, 'default', dateMapping, true)).toEqual( + 'September 4, 2011 - March 2, 2012' + ) + tk.reset() + }) - it('handles pluses and minuses', () => { - expect(dateStringToDayJs('d')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') - expect(dateStringToDayJs('+d')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') - expect(dateStringToDayJs('-d')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') + it('can have overridden date options', () => { + tk.freeze(new Date(1330688329321)) + expect( + dateFilterToText( + '-21d', + null, + 'default', + [{ key: 'Last 3 weeks', values: ['-21d'], getFormattedDate: () => 'custom formatted date' }], + true + ) + ).toEqual('custom formatted date') + tk.reset() + }) - expect(dateStringToDayJs('1d')?.utc(true).toISOString()).toEqual('2012-03-03T00:00:00.000Z') - expect(dateStringToDayJs('2d')?.utc(true).toISOString()).toEqual('2012-03-04T00:00:00.000Z') - expect(dateStringToDayJs('3d')?.utc(true).toISOString()).toEqual('2012-03-05T00:00:00.000Z') - expect(dateStringToDayJs('33d')?.utc(true).toISOString()).toEqual('2012-04-04T00:00:00.000Z') + it('can have overridden date format', () => { + const from = dayjs('2018-04-04T16:00:00.000Z').tz('America/New_York') + const to = dayjs('2018-04-09T15:05:00.000Z').tz('America/New_York') - expect(dateStringToDayJs('+1d')?.utc(true).toISOString()).toEqual('2012-03-03T00:00:00.000Z') - expect(dateStringToDayJs('+2d')?.utc(true).toISOString()).toEqual('2012-03-04T00:00:00.000Z') - expect(dateStringToDayJs('+3d')?.utc(true).toISOString()).toEqual('2012-03-05T00:00:00.000Z') - expect(dateStringToDayJs('+33d')?.utc(true).toISOString()).toEqual('2012-04-04T00:00:00.000Z') + expect(dateFilterToText(from, to, 'custom', dateMapping, true, 'YYYY-MM-DD hh:mm:ss')).toEqual( + '2018-04-04 12:00:00 - 2018-04-09 11:05:00' + ) + }) + }) + }) - expect(dateStringToDayJs('-1d')?.utc(true).toISOString()).toEqual('2012-03-01T00:00:00.000Z') - expect(dateStringToDayJs('-2d')?.utc(true).toISOString()).toEqual('2012-02-29T00:00:00.000Z') - expect(dateStringToDayJs('-3d')?.utc(true).toISOString()).toEqual('2012-02-28T00:00:00.000Z') - expect(dateStringToDayJs('-33d')?.utc(true).toISOString()).toEqual('2012-01-29T00:00:00.000Z') + describe('dateStringToDayJs', () => { + beforeEach(() => { + tk.freeze(1330688329321) // randomly chosen time on the 22nd of February 2022 + }) + afterEach(() => { + tk.reset() + }) - expect(dateStringToDayJs('-33m')?.utc(true).toISOString()).toEqual('2009-06-02T00:00:00.000Z') - expect(dateStringToDayJs('-33w')?.utc(true).toISOString()).toEqual('2011-07-15T00:00:00.000Z') - expect(dateStringToDayJs('-33q')?.utc(true).toISOString()).toEqual('2003-12-02T00:00:00.000Z') - expect(dateStringToDayJs('-33y')?.utc(true).toISOString()).toEqual('1979-03-02T00:00:00.000Z') - }) + it('handles various dates', () => { + expect(dateStringToDayJs('2022-02-22')?.utc(true).toISOString()).toEqual('2022-02-22T00:00:00.000Z') + expect(dateStringToDayJs('1999-12-31')?.utc(true).toISOString()).toEqual('1999-12-31T00:00:00.000Z') + }) - it('handles various start/end values', () => { - expect(dateStringToDayJs('dStart')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') - expect(dateStringToDayJs('dEnd')?.utc(true).toISOString()).toEqual('2012-03-02T23:59:59.999Z') - expect(dateStringToDayJs('wStart')?.utc(true).toISOString()).toEqual('2012-02-26T00:00:00.000Z') - expect(dateStringToDayJs('wEnd')?.utc(true).toISOString()).toEqual('2012-03-03T23:59:59.999Z') - expect(dateStringToDayJs('mStart')?.utc(true).toISOString()).toEqual('2012-03-01T00:00:00.000Z') - expect(dateStringToDayJs('mEnd')?.utc(true).toISOString()).toEqual('2012-03-31T23:59:59.999Z') - expect(dateStringToDayJs('qStart')?.utc(true).toISOString()).toEqual('2012-01-01T00:00:00.000Z') - expect(dateStringToDayJs('qEnd')?.utc(true).toISOString()).toEqual('2012-03-31T23:59:59.999Z') - expect(dateStringToDayJs('yStart')?.utc(true).toISOString()).toEqual('2012-01-01T00:00:00.000Z') - expect(dateStringToDayJs('yEnd')?.utc(true).toISOString()).toEqual('2012-12-31T23:59:59.999Z') - }) + it('handles various units', () => { + expect(dateStringToDayJs('d')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') + expect(dateStringToDayJs('m')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') + expect(dateStringToDayJs('w')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') + expect(dateStringToDayJs('q')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') + expect(dateStringToDayJs('y')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') + expect(dateStringToDayJs('x')).toEqual(null) + }) - it('handles various start/end values with units', () => { - expect(dateStringToDayJs('1dStart')?.utc(true).toISOString()).toEqual('2012-03-03T00:00:00.000Z') - expect(dateStringToDayJs('1dEnd')?.utc(true).toISOString()).toEqual('2012-03-03T23:59:59.999Z') + it('handles pluses and minuses', () => { + expect(dateStringToDayJs('d')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') + expect(dateStringToDayJs('+d')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') + expect(dateStringToDayJs('-d')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') + + expect(dateStringToDayJs('1d')?.utc(true).toISOString()).toEqual('2012-03-03T00:00:00.000Z') + expect(dateStringToDayJs('2d')?.utc(true).toISOString()).toEqual('2012-03-04T00:00:00.000Z') + expect(dateStringToDayJs('3d')?.utc(true).toISOString()).toEqual('2012-03-05T00:00:00.000Z') + expect(dateStringToDayJs('33d')?.utc(true).toISOString()).toEqual('2012-04-04T00:00:00.000Z') + + expect(dateStringToDayJs('+1d')?.utc(true).toISOString()).toEqual('2012-03-03T00:00:00.000Z') + expect(dateStringToDayJs('+2d')?.utc(true).toISOString()).toEqual('2012-03-04T00:00:00.000Z') + expect(dateStringToDayJs('+3d')?.utc(true).toISOString()).toEqual('2012-03-05T00:00:00.000Z') + expect(dateStringToDayJs('+33d')?.utc(true).toISOString()).toEqual('2012-04-04T00:00:00.000Z') + + expect(dateStringToDayJs('-1d')?.utc(true).toISOString()).toEqual('2012-03-01T00:00:00.000Z') + expect(dateStringToDayJs('-2d')?.utc(true).toISOString()).toEqual('2012-02-29T00:00:00.000Z') + expect(dateStringToDayJs('-3d')?.utc(true).toISOString()).toEqual('2012-02-28T00:00:00.000Z') + expect(dateStringToDayJs('-33d')?.utc(true).toISOString()).toEqual('2012-01-29T00:00:00.000Z') + + expect(dateStringToDayJs('-33m')?.utc(true).toISOString()).toEqual('2009-06-02T00:00:00.000Z') + expect(dateStringToDayJs('-33w')?.utc(true).toISOString()).toEqual('2011-07-15T00:00:00.000Z') + expect(dateStringToDayJs('-33q')?.utc(true).toISOString()).toEqual('2003-12-02T00:00:00.000Z') + expect(dateStringToDayJs('-33y')?.utc(true).toISOString()).toEqual('1979-03-02T00:00:00.000Z') + }) - expect(dateStringToDayJs('-1wStart')?.utc(true).toISOString()).toEqual('2012-02-19T00:00:00.000Z') - expect(dateStringToDayJs('-1wEnd')?.utc(true).toISOString()).toEqual('2012-02-25T23:59:59.999Z') + it('handles various start/end values', () => { + expect(dateStringToDayJs('dStart')?.utc(true).toISOString()).toEqual('2012-03-02T00:00:00.000Z') + expect(dateStringToDayJs('dEnd')?.utc(true).toISOString()).toEqual('2012-03-02T23:59:59.999Z') + expect(dateStringToDayJs('wStart')?.utc(true).toISOString()).toEqual('2012-02-26T00:00:00.000Z') + expect(dateStringToDayJs('wEnd')?.utc(true).toISOString()).toEqual('2012-03-03T23:59:59.999Z') + expect(dateStringToDayJs('mStart')?.utc(true).toISOString()).toEqual('2012-03-01T00:00:00.000Z') + expect(dateStringToDayJs('mEnd')?.utc(true).toISOString()).toEqual('2012-03-31T23:59:59.999Z') + expect(dateStringToDayJs('qStart')?.utc(true).toISOString()).toEqual('2012-01-01T00:00:00.000Z') + expect(dateStringToDayJs('qEnd')?.utc(true).toISOString()).toEqual('2012-03-31T23:59:59.999Z') + expect(dateStringToDayJs('yStart')?.utc(true).toISOString()).toEqual('2012-01-01T00:00:00.000Z') + expect(dateStringToDayJs('yEnd')?.utc(true).toISOString()).toEqual('2012-12-31T23:59:59.999Z') + }) - expect(dateStringToDayJs('12mStart')?.utc(true).toISOString()).toEqual('2013-03-01T00:00:00.000Z') - expect(dateStringToDayJs('12mEnd')?.utc(true).toISOString()).toEqual('2013-03-31T23:59:59.999Z') + it('handles various start/end values with units', () => { + expect(dateStringToDayJs('1dStart')?.utc(true).toISOString()).toEqual('2012-03-03T00:00:00.000Z') + expect(dateStringToDayJs('1dEnd')?.utc(true).toISOString()).toEqual('2012-03-03T23:59:59.999Z') - expect(dateStringToDayJs('-4qStart')?.utc(true).toISOString()).toEqual('2011-01-01T00:00:00.000Z') - expect(dateStringToDayJs('-4qEnd')?.utc(true).toISOString()).toEqual('2011-03-31T23:59:59.999Z') + expect(dateStringToDayJs('-1wStart')?.utc(true).toISOString()).toEqual('2012-02-19T00:00:00.000Z') + expect(dateStringToDayJs('-1wEnd')?.utc(true).toISOString()).toEqual('2012-02-25T23:59:59.999Z') - expect(dateStringToDayJs('0yStart')?.utc(true).toISOString()).toEqual('2012-01-01T00:00:00.000Z') - expect(dateStringToDayJs('0yEnd')?.utc(true).toISOString()).toEqual('2012-12-31T23:59:59.999Z') - }) -}) + expect(dateStringToDayJs('12mStart')?.utc(true).toISOString()).toEqual('2013-03-01T00:00:00.000Z') + expect(dateStringToDayJs('12mEnd')?.utc(true).toISOString()).toEqual('2013-03-31T23:59:59.999Z') -describe('getDefaultInterval', () => { - it('should return days for last 7 days', () => { - expect(getDefaultInterval('-7d', null)).toEqual('day') - }) + expect(dateStringToDayJs('-4qStart')?.utc(true).toISOString()).toEqual('2011-01-01T00:00:00.000Z') + expect(dateStringToDayJs('-4qEnd')?.utc(true).toISOString()).toEqual('2011-03-31T23:59:59.999Z') - it('should return hours for last 24 hours', () => { - expect(getDefaultInterval('-24h', null)).toEqual('hour') + expect(dateStringToDayJs('0yStart')?.utc(true).toISOString()).toEqual('2012-01-01T00:00:00.000Z') + expect(dateStringToDayJs('0yEnd')?.utc(true).toISOString()).toEqual('2012-12-31T23:59:59.999Z') + }) }) - it('should return days for month to date', () => { - expect(getDefaultInterval('mStart', null)).toEqual('day') - }) + describe('getDefaultInterval', () => { + it('should return days for last 7 days', () => { + expect(getDefaultInterval('-7d', null)).toEqual('day') + }) - it('should return month for year to date', () => { - expect(getDefaultInterval('yStart', null)).toEqual('month') - }) + it('should return hours for last 24 hours', () => { + expect(getDefaultInterval('-24h', null)).toEqual('hour') + }) - it('should return month for all time', () => { - expect(getDefaultInterval('all', null)).toEqual('month') - }) + it('should return days for month to date', () => { + expect(getDefaultInterval('mStart', null)).toEqual('day') + }) - it('should handle explicit dates 6 months apart', () => { - expect(getDefaultInterval('2023-10-01', '2023-04-01')).toEqual('month') - }) - it('should handle explicit dates a month apart', () => { - expect(getDefaultInterval('2023-10-01', '2023-09-01')).toEqual('week') - }) - it('should handle explicit dates a week apart', () => { - expect(getDefaultInterval('2023-10-01', '2023-09-25')).toEqual('day') - }) - it('should handle explicit dates a day apart', () => { - expect(getDefaultInterval('2023-10-02', '2023-10-01')).toEqual('hour') - }) - it('should handle explicit dates 12 hours apart', () => { - expect(getDefaultInterval('2023-10-01T18:00:00', '2023-10-01T6:00:00')).toEqual('hour') - }) -}) + it('should return month for year to date', () => { + expect(getDefaultInterval('yStart', null)).toEqual('month') + }) -describe('areDatesValidForInterval', () => { - it('should require interval to be month for all time', () => { - expect(areDatesValidForInterval('month', 'all', null)).toEqual(true) - expect(areDatesValidForInterval('week', 'all', null)).toEqual(false) - expect(areDatesValidForInterval('day', 'all', null)).toEqual(false) - expect(areDatesValidForInterval('hour', 'all', null)).toEqual(false) - }) - it('should return false if the dates are one interval apart', () => { - expect(areDatesValidForInterval('day', '-24h', null)).toEqual(false) - expect(areDatesValidForInterval('week', '-7d', null)).toEqual(false) - expect(areDatesValidForInterval('day', '-1d', null)).toEqual(false) - }) - it('should return true if the dates are two intervals apart', () => { - expect(areDatesValidForInterval('day', '-48h', null)).toEqual(true) - expect(areDatesValidForInterval('week', '-14d', null)).toEqual(true) - expect(areDatesValidForInterval('day', '-2d', null)).toEqual(true) - }) - it('should return false for hourly if over 2 weeks', () => { - expect(areDatesValidForInterval('hour', '-15d', null)).toEqual(false) - }) - it('should support explicit dates', () => { - expect(areDatesValidForInterval('month', '2023-08-01', '2023-11-01')).toEqual(true) - expect(areDatesValidForInterval('week', '2023-10-01', '2023-11-01')).toEqual(true) - expect(areDatesValidForInterval('day', '2023-10-16', '2023-11-01')).toEqual(true) - expect(areDatesValidForInterval('hour', '2023-11-01T12', '2023-11-01T18')).toEqual(true) - }) -}) + it('should return month for all time', () => { + expect(getDefaultInterval('all', null)).toEqual('month') + }) -describe('hexToRGBA()', () => { - it('converts hex to RGBA correctly', () => { - expect(hexToRGBA('#ff0000', 0.3)).toEqual('rgba(255,0,0,0.3)') - expect(hexToRGBA('#0000Cc', 0)).toEqual('rgba(0,0,204,0)') - expect(hexToRGBA('#5375ff', 1)).toEqual('rgba(83,117,255,1)') + it('should handle explicit dates 6 months apart', () => { + expect(getDefaultInterval('2023-10-01', '2023-04-01')).toEqual('month') + }) + it('should handle explicit dates a month apart', () => { + expect(getDefaultInterval('2023-10-01', '2023-09-01')).toEqual('week') + }) + it('should handle explicit dates a week apart', () => { + expect(getDefaultInterval('2023-10-01', '2023-09-25')).toEqual('day') + }) + it('should handle explicit dates a day apart', () => { + expect(getDefaultInterval('2023-10-02', '2023-10-01')).toEqual('hour') + }) + it('should handle explicit dates 12 hours apart', () => { + expect(getDefaultInterval('2023-10-01T18:00:00', '2023-10-01T6:00:00')).toEqual('hour') + }) }) -}) -describe('average()', () => { - it('calculates average correctly', () => { - expect(average([9, 4, 1, 3, 5, 7])).toEqual(4.8) - expect(average([72, 35, 68, 66, 70, 9, 81])).toEqual(57.3) // Tests rounding too - expect(average([86.4, 46.321, 45.304, 34.1, 147])).toEqual(71.8) // Tests rounding too + describe('areDatesValidForInterval', () => { + it('should require interval to be month for all time', () => { + expect(areDatesValidForInterval('month', 'all', null)).toEqual(true) + expect(areDatesValidForInterval('week', 'all', null)).toEqual(false) + expect(areDatesValidForInterval('day', 'all', null)).toEqual(false) + expect(areDatesValidForInterval('hour', 'all', null)).toEqual(false) + }) + it('should return false if the dates are one interval apart', () => { + expect(areDatesValidForInterval('day', '-24h', null)).toEqual(false) + expect(areDatesValidForInterval('week', '-7d', null)).toEqual(false) + expect(areDatesValidForInterval('day', '-1d', null)).toEqual(false) + }) + it('should return true if the dates are two intervals apart', () => { + expect(areDatesValidForInterval('day', '-48h', null)).toEqual(true) + expect(areDatesValidForInterval('week', '-14d', null)).toEqual(true) + expect(areDatesValidForInterval('day', '-2d', null)).toEqual(true) + }) + it('should return false for hourly if over 2 weeks', () => { + expect(areDatesValidForInterval('hour', '-15d', null)).toEqual(false) + }) + it('should support explicit dates', () => { + expect(areDatesValidForInterval('month', '2023-08-01', '2023-11-01')).toEqual(true) + expect(areDatesValidForInterval('week', '2023-10-01', '2023-11-01')).toEqual(true) + expect(areDatesValidForInterval('day', '2023-10-16', '2023-11-01')).toEqual(true) + expect(areDatesValidForInterval('hour', '2023-11-01T12', '2023-11-01T18')).toEqual(true) + }) }) -}) -describe('median()', () => { - it('returns middle number if array length is odd', () => { - expect(median([9, 4, 1, 3, 5, 7, 3, 6, 14])).toEqual(5) - }) - it('returns avg of middle numbers if array length is even', () => { - expect(median([9, 4, 0, 5, 7, 3, 6, 14])).toEqual(5.5) + describe('hexToRGBA()', () => { + it('converts hex to RGBA correctly', () => { + expect(hexToRGBA('#ff0000', 0.3)).toEqual('rgba(255,0,0,0.3)') + expect(hexToRGBA('#0000Cc', 0)).toEqual('rgba(0,0,204,0)') + expect(hexToRGBA('#5375ff', 1)).toEqual('rgba(83,117,255,1)') + }) }) -}) -describe('humanFriendlyLargeNumber()', () => { - it('returns the correct string', () => { - expect(humanFriendlyLargeNumber(1.234)).toEqual('1.23') - expect(humanFriendlyLargeNumber(12.34)).toEqual('12.3') - expect(humanFriendlyLargeNumber(123.4)).toEqual('123') - expect(humanFriendlyLargeNumber(1234)).toEqual('1.23K') - expect(humanFriendlyLargeNumber(12345)).toEqual('12.3K') - expect(humanFriendlyLargeNumber(123456)).toEqual('123K') - expect(humanFriendlyLargeNumber(1234567)).toEqual('1.23M') - expect(humanFriendlyLargeNumber(-1234567)).toEqual('-1.23M') - expect(humanFriendlyLargeNumber(-1)).toEqual('-1') - expect(humanFriendlyLargeNumber(-0.1)).toEqual('-0.1') - expect(humanFriendlyLargeNumber(0)).toEqual('0') - expect(humanFriendlyLargeNumber(NaN)).toEqual('NaN') - expect(humanFriendlyLargeNumber(Infinity)).toEqual('inf') - expect(humanFriendlyLargeNumber(-Infinity)).toEqual('-inf') - }) -}) -describe('humanFriendlyDuration()', () => { - it('returns correct value for <= 60', () => { - expect(humanFriendlyDuration(60)).toEqual('1m') - expect(humanFriendlyDuration(45)).toEqual('45s') - expect(humanFriendlyDuration(44.8)).toEqual('45s') - expect(humanFriendlyDuration(45.2)).toEqual('45s') - }) - it('returns correct value for 60 < t < 120', () => { - expect(humanFriendlyDuration(90)).toEqual('1m 30s') - }) - it('returns correct value for t > 120', () => { - expect(humanFriendlyDuration(360)).toEqual('6m') - }) - it('returns correct value for t >= 3600', () => { - expect(humanFriendlyDuration(3600)).toEqual('1h') - expect(humanFriendlyDuration(3601)).toEqual('1h 1s') - expect(humanFriendlyDuration(3961)).toEqual('1h 6m 1s') - expect(humanFriendlyDuration(3961.333)).toEqual('1h 6m 1s') - expect(humanFriendlyDuration(3961.666)).toEqual('1h 6m 2s') - }) - it('returns correct value for t >= 86400', () => { - expect(humanFriendlyDuration(86400)).toEqual('1d') - expect(humanFriendlyDuration(86400.12)).toEqual('1d') - }) - it('truncates to specified # of units', () => { - expect(humanFriendlyDuration(3961, 2)).toEqual('1h 6m') - expect(humanFriendlyDuration(30, 2)).toEqual('30s') // no change - expect(humanFriendlyDuration(30, 0)).toEqual('') // returns no units (useless) - }) - it('returns an empty string for nullish inputs', () => { - expect(humanFriendlyDuration('', 2)).toEqual('') - expect(humanFriendlyDuration(null, 2)).toEqual('') + describe('average()', () => { + it('calculates average correctly', () => { + expect(average([9, 4, 1, 3, 5, 7])).toEqual(4.8) + expect(average([72, 35, 68, 66, 70, 9, 81])).toEqual(57.3) // Tests rounding too + expect(average([86.4, 46.321, 45.304, 34.1, 147])).toEqual(71.8) // Tests rounding too + }) }) -}) -describe('colonDelimitedDuration()', () => { - it('returns correct value for <= 60', () => { - expect(colonDelimitedDuration(59.9)).toEqual('00:00:59') - expect(colonDelimitedDuration(60)).toEqual('00:01:00') - expect(colonDelimitedDuration(45)).toEqual('00:00:45') - }) - it('returns correct value for 60 < t < 120', () => { - expect(colonDelimitedDuration(90)).toEqual('00:01:30') - }) - it('returns correct value for t > 120', () => { - expect(colonDelimitedDuration(360)).toEqual('00:06:00') - expect(colonDelimitedDuration(360.3233)).toEqual('00:06:00') - expect(colonDelimitedDuration(360.782)).toEqual('00:06:00') - }) - it('returns correct value for t >= 3600', () => { - expect(colonDelimitedDuration(3600)).toEqual('01:00:00') - expect(colonDelimitedDuration(3601)).toEqual('01:00:01') - expect(colonDelimitedDuration(3961)).toEqual('01:06:01') - }) - it('returns correct value for t >= 86400', () => { - expect(colonDelimitedDuration(86400)).toEqual('24:00:00') - expect(colonDelimitedDuration(90000)).toEqual('25:00:00') - }) - it('returns correct value for numUnits < 3', () => { - expect(colonDelimitedDuration(86400, 2)).toEqual('1440:00') - expect(colonDelimitedDuration(86400, 1)).toEqual('86400') - }) - it('returns correct value for numUnits >= 4', () => { - expect(colonDelimitedDuration(86400, 4)).toEqual('01:00:00:00') - expect(colonDelimitedDuration(90000, 4)).toEqual('01:01:00:00') - expect(colonDelimitedDuration(90061, 4)).toEqual('01:01:01:01') - expect(colonDelimitedDuration(604800, 5)).toEqual('01:00:00:00:00') - expect(colonDelimitedDuration(604800, 6)).toEqual('01:00:00:00:00') - expect(colonDelimitedDuration(604800.222, 5)).toEqual('01:00:00:00:00') - expect(colonDelimitedDuration(604800.999, 6)).toEqual('01:00:00:00:00') - }) - it('returns the smallest possible for numUnits = null', () => { - expect(colonDelimitedDuration(59, null)).toEqual('00:59') - expect(colonDelimitedDuration(3599, null)).toEqual('59:59') - expect(colonDelimitedDuration(3600, null)).toEqual('01:00:00') - }) - it('returns an empty string for nullish inputs', () => { - expect(colonDelimitedDuration('')).toEqual('') - expect(colonDelimitedDuration(null)).toEqual('') - expect(colonDelimitedDuration(undefined)).toEqual('') + describe('median()', () => { + it('returns middle number if array length is odd', () => { + expect(median([9, 4, 1, 3, 5, 7, 3, 6, 14])).toEqual(5) + }) + it('returns avg of middle numbers if array length is even', () => { + expect(median([9, 4, 0, 5, 7, 3, 6, 14])).toEqual(5.5) + }) }) -}) -describe('reverseColonDelimitedDuration()', () => { - it('returns correct value', () => { - expect(reverseColonDelimitedDuration('59')).toEqual(59) - expect(reverseColonDelimitedDuration('59:59')).toEqual(3599) - expect(reverseColonDelimitedDuration('23:59:59')).toEqual(86399) + describe('humanFriendlyLargeNumber()', () => { + it('returns the correct string', () => { + expect(humanFriendlyLargeNumber(1.234)).toEqual('1.23') + expect(humanFriendlyLargeNumber(12.34)).toEqual('12.3') + expect(humanFriendlyLargeNumber(123.4)).toEqual('123') + expect(humanFriendlyLargeNumber(1234)).toEqual('1.23K') + expect(humanFriendlyLargeNumber(12345)).toEqual('12.3K') + expect(humanFriendlyLargeNumber(123456)).toEqual('123K') + expect(humanFriendlyLargeNumber(1234567)).toEqual('1.23M') + expect(humanFriendlyLargeNumber(-1234567)).toEqual('-1.23M') + expect(humanFriendlyLargeNumber(-1)).toEqual('-1') + expect(humanFriendlyLargeNumber(-0.1)).toEqual('-0.1') + expect(humanFriendlyLargeNumber(0)).toEqual('0') + expect(humanFriendlyLargeNumber(NaN)).toEqual('NaN') + expect(humanFriendlyLargeNumber(Infinity)).toEqual('inf') + expect(humanFriendlyLargeNumber(-Infinity)).toEqual('-inf') + }) }) - it('returns an null for bad values', () => { - expect(reverseColonDelimitedDuration('1232123')).toEqual(null) - expect(reverseColonDelimitedDuration('AA:AA:AA')).toEqual(null) - expect(reverseColonDelimitedDuration(undefined)).toEqual(null) + describe('humanFriendlyDuration()', () => { + it('returns correct value for <= 60', () => { + expect(humanFriendlyDuration(60)).toEqual('1m') + expect(humanFriendlyDuration(45)).toEqual('45s') + expect(humanFriendlyDuration(44.8)).toEqual('45s') + expect(humanFriendlyDuration(45.2)).toEqual('45s') + }) + it('returns correct value for 60 < t < 120', () => { + expect(humanFriendlyDuration(90)).toEqual('1m 30s') + }) + it('returns correct value for t > 120', () => { + expect(humanFriendlyDuration(360)).toEqual('6m') + }) + it('returns correct value for t >= 3600', () => { + expect(humanFriendlyDuration(3600)).toEqual('1h') + expect(humanFriendlyDuration(3601)).toEqual('1h 1s') + expect(humanFriendlyDuration(3961)).toEqual('1h 6m 1s') + expect(humanFriendlyDuration(3961.333)).toEqual('1h 6m 1s') + expect(humanFriendlyDuration(3961.666)).toEqual('1h 6m 2s') + }) + it('returns correct value for t >= 86400', () => { + expect(humanFriendlyDuration(86400)).toEqual('1d') + expect(humanFriendlyDuration(86400.12)).toEqual('1d') + }) + it('truncates to specified # of units', () => { + expect(humanFriendlyDuration(3961, 2)).toEqual('1h 6m') + expect(humanFriendlyDuration(30, 2)).toEqual('30s') // no change + expect(humanFriendlyDuration(30, 0)).toEqual('') // returns no units (useless) + }) + it('returns an empty string for nullish inputs', () => { + expect(humanFriendlyDuration('', 2)).toEqual('') + expect(humanFriendlyDuration(null, 2)).toEqual('') + }) }) -}) -describe('areObjectValuesEmpty()', () => { - it('returns correct value for objects with empty values', () => { - expect(areObjectValuesEmpty({ a: '', b: null, c: undefined })).toEqual(true) - expect(areObjectValuesEmpty({ a: undefined, b: undefined })).toEqual(true) - expect(areObjectValuesEmpty({})).toEqual(true) - }) - it('returns correct value for objects with at least one non-empty value', () => { - expect(areObjectValuesEmpty({ a: '', b: null, c: 'hello' })).toEqual(false) - expect(areObjectValuesEmpty({ a: true, b: 'hello' })).toEqual(false) - expect(areObjectValuesEmpty('hello' as any)).toEqual(false) - expect(areObjectValuesEmpty(null as any)).toEqual(false) + describe('colonDelimitedDuration()', () => { + it('returns correct value for <= 60', () => { + expect(colonDelimitedDuration(59.9)).toEqual('00:00:59') + expect(colonDelimitedDuration(60)).toEqual('00:01:00') + expect(colonDelimitedDuration(45)).toEqual('00:00:45') + }) + it('returns correct value for 60 < t < 120', () => { + expect(colonDelimitedDuration(90)).toEqual('00:01:30') + }) + it('returns correct value for t > 120', () => { + expect(colonDelimitedDuration(360)).toEqual('00:06:00') + expect(colonDelimitedDuration(360.3233)).toEqual('00:06:00') + expect(colonDelimitedDuration(360.782)).toEqual('00:06:00') + }) + it('returns correct value for t >= 3600', () => { + expect(colonDelimitedDuration(3600)).toEqual('01:00:00') + expect(colonDelimitedDuration(3601)).toEqual('01:00:01') + expect(colonDelimitedDuration(3961)).toEqual('01:06:01') + }) + it('returns correct value for t >= 86400', () => { + expect(colonDelimitedDuration(86400)).toEqual('24:00:00') + expect(colonDelimitedDuration(90000)).toEqual('25:00:00') + }) + it('returns correct value for numUnits < 3', () => { + expect(colonDelimitedDuration(86400, 2)).toEqual('1440:00') + expect(colonDelimitedDuration(86400, 1)).toEqual('86400') + }) + it('returns correct value for numUnits >= 4', () => { + expect(colonDelimitedDuration(86400, 4)).toEqual('01:00:00:00') + expect(colonDelimitedDuration(90000, 4)).toEqual('01:01:00:00') + expect(colonDelimitedDuration(90061, 4)).toEqual('01:01:01:01') + expect(colonDelimitedDuration(604800, 5)).toEqual('01:00:00:00:00') + expect(colonDelimitedDuration(604800, 6)).toEqual('01:00:00:00:00') + expect(colonDelimitedDuration(604800.222, 5)).toEqual('01:00:00:00:00') + expect(colonDelimitedDuration(604800.999, 6)).toEqual('01:00:00:00:00') + }) + it('returns the smallest possible for numUnits = null', () => { + expect(colonDelimitedDuration(59, null)).toEqual('00:59') + expect(colonDelimitedDuration(3599, null)).toEqual('59:59') + expect(colonDelimitedDuration(3600, null)).toEqual('01:00:00') + }) + it('returns an empty string for nullish inputs', () => { + expect(colonDelimitedDuration('')).toEqual('') + expect(colonDelimitedDuration(null)).toEqual('') + expect(colonDelimitedDuration(undefined)).toEqual('') + }) }) -}) -describe('ensureStringIsNotBlank()', () => { - it('handles unusual input', () => { - expect(ensureStringIsNotBlank(null)).toEqual(null) - expect(ensureStringIsNotBlank({} as any)).toEqual(null) - expect(ensureStringIsNotBlank(undefined)).toEqual(null) - expect(ensureStringIsNotBlank(true as any)).toEqual(null) - }) - it('handles blank strings as expected', () => { - expect(ensureStringIsNotBlank('')).toEqual(null) - expect(ensureStringIsNotBlank(' ')).toEqual(null) - }) - it('handles happy case', () => { - expect(ensureStringIsNotBlank('happyboy')).toEqual('happyboy') - expect(ensureStringIsNotBlank(' happy boy ')).toEqual(' happy boy ') + describe('reverseColonDelimitedDuration()', () => { + it('returns correct value', () => { + expect(reverseColonDelimitedDuration('59')).toEqual(59) + expect(reverseColonDelimitedDuration('59:59')).toEqual(3599) + expect(reverseColonDelimitedDuration('23:59:59')).toEqual(86399) + }) + it('returns an null for bad values', () => { + expect(reverseColonDelimitedDuration('1232123')).toEqual(null) + expect(reverseColonDelimitedDuration('AA:AA:AA')).toEqual(null) + expect(reverseColonDelimitedDuration(undefined)).toEqual(null) + }) }) -}) -describe('objectDiffShallow()', () => { - it('obj1 + result = obj2', () => { - expect(objectDiffShallow({ b: '4' }, { b: '3', a: '2' })).toStrictEqual({ b: '3', a: '2' }) - expect(objectDiffShallow({ b: '4', c: '12' }, { b: '3', a: '2' })).toStrictEqual({ - b: '3', - a: '2', - c: undefined, + describe('areObjectValuesEmpty()', () => { + it('returns correct value for objects with empty values', () => { + expect(areObjectValuesEmpty({ a: '', b: null, c: undefined })).toEqual(true) + expect(areObjectValuesEmpty({ a: undefined, b: undefined })).toEqual(true) + expect(areObjectValuesEmpty({})).toEqual(true) + }) + it('returns correct value for objects with at least one non-empty value', () => { + expect(areObjectValuesEmpty({ a: '', b: null, c: 'hello' })).toEqual(false) + expect(areObjectValuesEmpty({ a: true, b: 'hello' })).toEqual(false) + expect(areObjectValuesEmpty('hello' as any)).toEqual(false) + expect(areObjectValuesEmpty(null as any)).toEqual(false) }) }) -}) -describe('objectClean()', () => { - it('removes undefined values', () => { - expect(objectClean({ a: 1, b: 'b', c: null, d: {}, e: [], f: undefined })).toStrictEqual({ - a: 1, - b: 'b', - c: null, - d: {}, - e: [], + describe('ensureStringIsNotBlank()', () => { + it('handles unusual input', () => { + expect(ensureStringIsNotBlank(null)).toEqual(null) + expect(ensureStringIsNotBlank({} as any)).toEqual(null) + expect(ensureStringIsNotBlank(undefined)).toEqual(null) + expect(ensureStringIsNotBlank(true as any)).toEqual(null) + }) + it('handles blank strings as expected', () => { + expect(ensureStringIsNotBlank('')).toEqual(null) + expect(ensureStringIsNotBlank(' ')).toEqual(null) + }) + it('handles happy case', () => { + expect(ensureStringIsNotBlank('happyboy')).toEqual('happyboy') + expect(ensureStringIsNotBlank(' happy boy ')).toEqual(' happy boy ') }) }) -}) -describe('objectCleanWithEmpty()', () => { - it('removes undefined and empty values', () => { - expect( - objectCleanWithEmpty({ a: 1, b: 'b', c: null, d: {}, e: [], f: undefined, g: { x: 1 }, h: [1] }) - ).toStrictEqual({ - a: 1, - b: 'b', - c: null, - g: { x: 1 }, - h: [1], + describe('objectDiffShallow()', () => { + it('obj1 + result = obj2', () => { + expect(objectDiffShallow({ b: '4' }, { b: '3', a: '2' })).toStrictEqual({ b: '3', a: '2' }) + expect(objectDiffShallow({ b: '4', c: '12' }, { b: '3', a: '2' })).toStrictEqual({ + b: '3', + a: '2', + c: undefined, + }) }) }) -}) -describe('eventToName()', () => { - const baseEvent = { - elements: [], - event: '', - properties: {}, - person: {}, - } as any as EventType - - it('handles page events as expected', () => { - expect(eventToDescription({ ...baseEvent, event: '$pageview', properties: { $pathname: '/hello' } })).toEqual( - '/hello' - ) - expect(eventToDescription({ ...baseEvent, event: '$pageleave', properties: { $pathname: '/bye' } })).toEqual( - '/bye' - ) - }) - - it('handles no text autocapture as expected', () => { - expect( - eventToDescription({ - ...baseEvent, - event: '$autocapture', - properties: { $event_type: 'click' }, + describe('objectClean()', () => { + it('removes undefined values', () => { + expect(objectClean({ a: 1, b: 'b', c: null, d: {}, e: [], f: undefined })).toStrictEqual({ + a: 1, + b: 'b', + c: null, + d: {}, + e: [], }) - ).toEqual('clicked element') + }) }) - it('handles long form autocapture as expected', () => { - expect( - eventToDescription({ - ...baseEvent, - event: '$autocapture', - properties: { $event_type: 'click' }, - elements: [{ tag_name: 'button', text: 'hello' } as ElementType], + describe('objectCleanWithEmpty()', () => { + it('removes undefined and empty values', () => { + expect( + objectCleanWithEmpty({ a: 1, b: 'b', c: null, d: {}, e: [], f: undefined, g: { x: 1 }, h: [1] }) + ).toStrictEqual({ + a: 1, + b: 'b', + c: null, + g: { x: 1 }, + h: [1], }) - ).toEqual('clicked button with text "hello"') + }) }) - it('handles short form autocapture as expected', () => { - expect( - eventToDescription( - { + describe('eventToName()', () => { + const baseEvent = { + elements: [], + event: '', + properties: {}, + person: {}, + } as any as EventType + + it('handles page events as expected', () => { + expect( + eventToDescription({ ...baseEvent, event: '$pageview', properties: { $pathname: '/hello' } }) + ).toEqual('/hello') + expect( + eventToDescription({ ...baseEvent, event: '$pageleave', properties: { $pathname: '/bye' } }) + ).toEqual('/bye') + }) + + it('handles no text autocapture as expected', () => { + expect( + eventToDescription({ + ...baseEvent, + event: '$autocapture', + properties: { $event_type: 'click' }, + }) + ).toEqual('clicked element') + }) + + it('handles long form autocapture as expected', () => { + expect( + eventToDescription({ ...baseEvent, event: '$autocapture', properties: { $event_type: 'click' }, elements: [{ tag_name: 'button', text: 'hello' } as ElementType], - }, - true - ) - ).toEqual('clicked "hello"') + }) + ).toEqual('clicked button with text "hello"') + }) + + it('handles short form autocapture as expected', () => { + expect( + eventToDescription( + { + ...baseEvent, + event: '$autocapture', + properties: { $event_type: 'click' }, + elements: [{ tag_name: 'button', text: 'hello' } as ElementType], + }, + true + ) + ).toEqual('clicked "hello"') + }) + + it('handles unknown event/action', () => { + expect( + eventToDescription({ + ...baseEvent, + event: 'custom event/action', + }) + ).toEqual('custom event/action') + }) }) - it('handles unknown event/action', () => { - expect( - eventToDescription({ - ...baseEvent, - event: 'custom event/action', + describe('{floor|ceil}MsToClosestSecond()', () => { + describe('ceil', () => { + it('handles ms as expected', () => { + expect(ceilMsToClosestSecond(10532)).toEqual(11000) + expect(ceilMsToClosestSecond(1500)).toEqual(2000) + expect(ceilMsToClosestSecond(500)).toEqual(1000) + expect(ceilMsToClosestSecond(-10532)).toEqual(-10000) + expect(ceilMsToClosestSecond(-1500)).toEqual(-1000) + expect(ceilMsToClosestSecond(-500)).toEqual(-0) }) - ).toEqual('custom event/action') - }) -}) + it('handles whole seconds as expected', () => { + expect(ceilMsToClosestSecond(0)).toEqual(0) + expect(ceilMsToClosestSecond(1000)).toEqual(1000) + expect(ceilMsToClosestSecond(-1000)).toEqual(-1000) + }) + }) -describe('{floor|ceil}MsToClosestSecond()', () => { - describe('ceil', () => { - it('handles ms as expected', () => { - expect(ceilMsToClosestSecond(10532)).toEqual(11000) - expect(ceilMsToClosestSecond(1500)).toEqual(2000) - expect(ceilMsToClosestSecond(500)).toEqual(1000) - expect(ceilMsToClosestSecond(-10532)).toEqual(-10000) - expect(ceilMsToClosestSecond(-1500)).toEqual(-1000) - expect(ceilMsToClosestSecond(-500)).toEqual(-0) - }) - it('handles whole seconds as expected', () => { - expect(ceilMsToClosestSecond(0)).toEqual(0) - expect(ceilMsToClosestSecond(1000)).toEqual(1000) - expect(ceilMsToClosestSecond(-1000)).toEqual(-1000) - }) - }) - - describe('floor', () => { - it('handles ms as expected', () => { - expect(floorMsToClosestSecond(10532)).toEqual(10000) - expect(floorMsToClosestSecond(1500)).toEqual(1000) - expect(floorMsToClosestSecond(500)).toEqual(0) - expect(floorMsToClosestSecond(-10532)).toEqual(-11000) - expect(floorMsToClosestSecond(-1500)).toEqual(-2000) - expect(floorMsToClosestSecond(-500)).toEqual(-1000) - }) - it('handles whole seconds as expected', () => { - expect(floorMsToClosestSecond(0)).toEqual(0) - expect(floorMsToClosestSecond(1000)).toEqual(1000) - expect(floorMsToClosestSecond(-1000)).toEqual(-1000) - }) - }) - - describe('choosing an operator for taxonomic filters', () => { - const testCases = [ - { propertyType: PropertyType.DateTime, expected: dateTimeOperatorMap }, - { propertyType: PropertyType.String, expected: stringOperatorMap }, - { propertyType: PropertyType.Numeric, expected: numericOperatorMap }, - { propertyType: PropertyType.Boolean, expected: booleanOperatorMap }, - { propertyType: PropertyType.Duration, expected: durationOperatorMap }, - { propertyType: PropertyType.Selector, expected: selectorOperatorMap }, - { propertyType: undefined, expected: genericOperatorMap }, - ] - testCases.forEach((testcase) => { - it(`correctly maps ${testcase.propertyType} to operator options`, () => { - expect(chooseOperatorMap(testcase.propertyType)).toEqual(testcase.expected) + describe('floor', () => { + it('handles ms as expected', () => { + expect(floorMsToClosestSecond(10532)).toEqual(10000) + expect(floorMsToClosestSecond(1500)).toEqual(1000) + expect(floorMsToClosestSecond(500)).toEqual(0) + expect(floorMsToClosestSecond(-10532)).toEqual(-11000) + expect(floorMsToClosestSecond(-1500)).toEqual(-2000) + expect(floorMsToClosestSecond(-500)).toEqual(-1000) + }) + it('handles whole seconds as expected', () => { + expect(floorMsToClosestSecond(0)).toEqual(0) + expect(floorMsToClosestSecond(1000)).toEqual(1000) + expect(floorMsToClosestSecond(-1000)).toEqual(-1000) }) }) - }) -}) -describe('calculateDays', () => { - it('1 day to 1 day', () => { - expect(calculateDays(1, TimeUnitType.Day)).toEqual(1) - }) - it('1 week to 7 days', () => { - expect(calculateDays(1, TimeUnitType.Week)).toEqual(7) - }) - it('1 month to 30 days', () => { - expect(calculateDays(1, TimeUnitType.Month)).toEqual(30) - }) - it('1 year to 365 days', () => { - expect(calculateDays(1, TimeUnitType.Year)).toEqual(365) + describe('choosing an operator for taxonomic filters', () => { + const testCases = [ + { propertyType: PropertyType.DateTime, expected: dateTimeOperatorMap }, + { propertyType: PropertyType.String, expected: stringOperatorMap }, + { propertyType: PropertyType.Numeric, expected: numericOperatorMap }, + { propertyType: PropertyType.Boolean, expected: booleanOperatorMap }, + { propertyType: PropertyType.Duration, expected: durationOperatorMap }, + { propertyType: PropertyType.Selector, expected: selectorOperatorMap }, + { propertyType: undefined, expected: genericOperatorMap }, + ] + testCases.forEach((testcase) => { + it(`correctly maps ${testcase.propertyType} to operator options`, () => { + expect(chooseOperatorMap(testcase.propertyType)).toEqual(testcase.expected) + }) + }) + }) }) -}) -describe('range', () => { - it('creates simple range', () => { - expect(range(4)).toEqual([0, 1, 2, 3]) + describe('calculateDays', () => { + it('1 day to 1 day', () => { + expect(calculateDays(1, TimeUnitType.Day)).toEqual(1) + }) + it('1 week to 7 days', () => { + expect(calculateDays(1, TimeUnitType.Week)).toEqual(7) + }) + it('1 month to 30 days', () => { + expect(calculateDays(1, TimeUnitType.Month)).toEqual(30) + }) + it('1 year to 365 days', () => { + expect(calculateDays(1, TimeUnitType.Year)).toEqual(365) + }) }) - it('creates offset range', () => { - expect(range(1, 5)).toEqual([1, 2, 3, 4]) + describe('range', () => { + it('creates simple range', () => { + expect(range(4)).toEqual([0, 1, 2, 3]) + }) + + it('creates offset range', () => { + expect(range(1, 5)).toEqual([1, 2, 3, 4]) + }) }) -}) -describe('time ranges', () => { - it('is less than or equal to 12 hours', () => { - expect(is12HoursOrLess('-0h')).toBeTruthy() - expect(is12HoursOrLess('-1h')).toBeTruthy() - expect(is12HoursOrLess('-12h')).toBeTruthy() - expect(is12HoursOrLess('-13h')).toBeFalsy() - - expect(is12HoursOrLess('-24h')).toBeFalsy() - expect(is12HoursOrLess('-30h')).toBeFalsy() - expect(is12HoursOrLess('-47h')).toBeFalsy() - expect(is12HoursOrLess('-111h')).toBeFalsy() - - expect(is12HoursOrLess('-1.123h')).toBeFalsy() - expect(is12HoursOrLess('1.123h')).toBeFalsy() - expect(is12HoursOrLess('-ab1-13h')).toBeFalsy() - expect(is12HoursOrLess('-1d')).toBeFalsy() - expect(is12HoursOrLess('-1w')).toBeFalsy() - expect(is12HoursOrLess('-1h-2h')).toBeFalsy() - }) - - it('is less than 2 days', () => { - expect(isLessThan2Days('-0h')).toBeTruthy() - expect(isLessThan2Days('-1h')).toBeTruthy() - expect(isLessThan2Days('-12h')).toBeTruthy() - expect(isLessThan2Days('-24h')).toBeTruthy() - expect(isLessThan2Days('-30h')).toBeTruthy() - expect(isLessThan2Days('-47h')).toBeTruthy() - - expect(isLessThan2Days('-48h')).toBeFalsy() - expect(isLessThan2Days('-49h')).toBeFalsy() - expect(isLessThan2Days('0h')).toBeFalsy() - expect(isLessThan2Days('1h')).toBeFalsy() - expect(isLessThan2Days('48h')).toBeFalsy() - expect(isLessThan2Days('-13.123h')).toBeFalsy() - expect(isLessThan2Days('13.123h')).toBeFalsy() - expect(isLessThan2Days('-ab1-13h')).toBeFalsy() - expect(isLessThan2Days('-1d-1h')).toBeFalsy() + describe('time ranges', () => { + it('is less than or equal to 12 hours', () => { + expect(is12HoursOrLess('-0h')).toBeTruthy() + expect(is12HoursOrLess('-1h')).toBeTruthy() + expect(is12HoursOrLess('-12h')).toBeTruthy() + expect(is12HoursOrLess('-13h')).toBeFalsy() + + expect(is12HoursOrLess('-24h')).toBeFalsy() + expect(is12HoursOrLess('-30h')).toBeFalsy() + expect(is12HoursOrLess('-47h')).toBeFalsy() + expect(is12HoursOrLess('-111h')).toBeFalsy() + + expect(is12HoursOrLess('-1.123h')).toBeFalsy() + expect(is12HoursOrLess('1.123h')).toBeFalsy() + expect(is12HoursOrLess('-ab1-13h')).toBeFalsy() + expect(is12HoursOrLess('-1d')).toBeFalsy() + expect(is12HoursOrLess('-1w')).toBeFalsy() + expect(is12HoursOrLess('-1h-2h')).toBeFalsy() + }) + + it('is less than 2 days', () => { + expect(isLessThan2Days('-0h')).toBeTruthy() + expect(isLessThan2Days('-1h')).toBeTruthy() + expect(isLessThan2Days('-12h')).toBeTruthy() + expect(isLessThan2Days('-24h')).toBeTruthy() + expect(isLessThan2Days('-30h')).toBeTruthy() + expect(isLessThan2Days('-47h')).toBeTruthy() + + expect(isLessThan2Days('-48h')).toBeFalsy() + expect(isLessThan2Days('-49h')).toBeFalsy() + expect(isLessThan2Days('0h')).toBeFalsy() + expect(isLessThan2Days('1h')).toBeFalsy() + expect(isLessThan2Days('48h')).toBeFalsy() + expect(isLessThan2Days('-13.123h')).toBeFalsy() + expect(isLessThan2Days('13.123h')).toBeFalsy() + expect(isLessThan2Days('-ab1-13h')).toBeFalsy() + expect(isLessThan2Days('-1d-1h')).toBeFalsy() + }) }) -}) -test('shortTimezone', () => { - expect(shortTimeZone('UTC')).toEqual('UTC') - // All timezones below don't observe DST for simplicity - expect(shortTimeZone('America/Phoenix')).toEqual('MST') - expect(shortTimeZone('Europe/Moscow')).toEqual('UTC+3') - expect(shortTimeZone('Asia/Tokyo')).toEqual('UTC+9') + test('shortTimezone', () => { + expect(shortTimeZone('UTC')).toEqual('UTC') + // All timezones below don't observe DST for simplicity + expect(shortTimeZone('America/Phoenix')).toEqual('MST') + expect(shortTimeZone('Europe/Moscow')).toEqual('UTC+3') + expect(shortTimeZone('Asia/Tokyo')).toEqual('UTC+9') + }) }) diff --git a/frontend/src/lib/utils.tsx b/frontend/src/lib/utils.tsx index fe85640a88b2f..b95beb0d7ecfb 100644 --- a/frontend/src/lib/utils.tsx +++ b/frontend/src/lib/utils.tsx @@ -613,7 +613,7 @@ export function isURL(input: any): boolean { if (!input || typeof input !== 'string') { return false } - const regexp = /^http(s)?:\/\/[\w*.-]+[\w*.-]+[\w\-._~:/?#[\]@%!$&'()*+,;=]+$/ + const regexp = /^(http|capacitor|https):\/\/[\w*.-]+[\w*.-]+[\w\-._~:/?#[\]@%!$&'()*+,;=]+$/ return !!input.trim().match(regexp) }