diff --git a/unittest/unit/update-bcd.ts b/unittest/unit/update-bcd.ts index c6c12a769..f2e9efea5 100644 --- a/unittest/unit/update-bcd.ts +++ b/unittest/unit/update-bcd.ts @@ -817,7 +817,7 @@ describe('BCD updater', () => { let bcdCopy; beforeEach(() => { - bcdCopy = JSON.parse(JSON.stringify(bcd)); + bcdCopy = clone(bcd); }); it('normal', () => { @@ -996,6 +996,212 @@ describe('BCD updater', () => { }); }); + describe('mirror', () => { + const chromeAndroid86UaString = + 'Mozilla/5.0 (Linux; Android 10; SM-G960U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.5112.97 Mobile Safari/537.36'; + const browsers: any = { + chrome: {name: 'Chrome', releases: {85: {}, 86: {}}}, + chrome_android: { + name: 'Chrome Android', + upstream: 'chrome', + releases: {86: {}} + } + }; + + const bcdFromSupport = (support) => ({ + api: {FakeInterface: {__compat: {support}}} + }); + + /** + * Create a BCD data structure for an arbitrary web platform feature + * based on support data for Chrome and Chrome Android and test result + * data for Chrome Android. This utility invokes the `update` function + * and is designed to observe the behavior of the "mirror" support value. + * + * @return {BCD} + */ + const mirroringCase = ({support, downstreamResult}) => { + const reports: Report[] = [ + { + __version: '0.3.1', + results: { + 'https://mdn-bcd-collector.appspot.com/tests/': [ + { + name: 'api.FakeInterface', + exposure: 'Window', + result: downstreamResult + } + ] + }, + userAgent: chromeAndroid86UaString + } + ]; + const supportMatrix = getSupportMatrix(reports, browsers, []); + const bcd = bcdFromSupport(support); + update(bcd, supportMatrix, {}); + return bcd; + }; + + describe('supported upstream (without flags)', () => { + it('supported in downstream test results', () => { + const actual = mirroringCase({ + support: { + chrome: {version_added: '86'}, + chrome_android: 'mirror' + }, + downstreamResult: true + }); + assert.deepEqual( + actual, + bcdFromSupport({ + chrome: {version_added: '86'}, + chrome_android: 'mirror' + }) + ); + }); + + it('unsupported in downstream test results', () => { + const actual = mirroringCase({ + support: { + chrome: {version_added: '85'}, + chrome_android: 'mirror' + }, + downstreamResult: false + }); + assert.deepEqual( + actual, + bcdFromSupport({ + chrome: {version_added: '85'}, + chrome_android: {version_added: false} + }) + ); + }); + + it('omitted from downstream test results', () => { + const actual = mirroringCase({ + support: { + chrome: {version_added: '85'}, + chrome_android: 'mirror' + }, + downstreamResult: null + }); + assert.deepEqual( + actual, + bcdFromSupport({ + chrome: {version_added: '85'}, + chrome_android: 'mirror' + }) + ); + }); + }); + + describe('supported upstream (with flags)', () => { + it('supported in downstream test results', () => { + const actual = mirroringCase({ + support: { + chrome: {version_added: '85', flags: [{}]}, + chrome_android: 'mirror' + }, + downstreamResult: true + }); + assert.deepEqual( + actual, + bcdFromSupport({ + chrome: {version_added: '85', flags: [{}]}, + chrome_android: {version_added: '86'} + }) + ); + }); + + it('unsupported in downstream test results', () => { + const actual = mirroringCase({ + support: { + chrome: {version_added: '85', flags: [{}]}, + chrome_android: 'mirror' + }, + downstreamResult: false + }); + assert.deepEqual( + actual, + bcdFromSupport({ + chrome: {version_added: '85', flags: [{}]}, + chrome_android: 'mirror' + }) + ); + }); + + it('omitted from downstream test results', () => { + const actual = mirroringCase({ + support: { + chrome: {version_added: '85', flags: [{}]}, + chrome_android: 'mirror' + }, + downstreamResult: null + }); + assert.deepEqual( + actual, + bcdFromSupport({ + chrome: {version_added: '85', flags: [{}]}, + chrome_android: 'mirror' + }) + ); + }); + }); + + describe('unsupported upstream', () => { + it('supported in downstream test results', () => { + const actual = mirroringCase({ + support: { + chrome: {version_added: false}, + chrome_android: 'mirror' + }, + downstreamResult: true + }); + assert.deepEqual( + actual, + bcdFromSupport({ + chrome: {version_added: false}, + chrome_android: {version_added: '86'} + }) + ); + }); + + it('unsupported in downstream test results', () => { + const actual = mirroringCase({ + support: { + chrome: {version_added: false}, + chrome_android: 'mirror' + }, + downstreamResult: false + }); + assert.deepEqual( + actual, + bcdFromSupport({ + chrome: {version_added: false}, + chrome_android: 'mirror' + }) + ); + }); + + it('omitted from downstream test results', () => { + const actual = mirroringCase({ + support: { + chrome: {version_added: false}, + chrome_android: 'mirror' + }, + downstreamResult: null + }); + assert.deepEqual( + actual, + bcdFromSupport({ + chrome: {version_added: false}, + chrome_android: 'mirror' + }) + ); + }); + }); + }); + it('does not report a modification when results corroborate existing data', () => { const firefox92UaString = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:92.0) Gecko/20100101 Firefox/92.0'; @@ -1013,7 +1219,7 @@ describe('BCD updater', () => { firefox: {name: 'Firefox', releases: {92: {}}} } as unknown as Browsers }; - const finalBcd = JSON.parse(JSON.stringify(initialBcd)); + const finalBcd = clone(initialBcd); const report: Report = { __version: '0.3.1', results: { diff --git a/update-bcd.ts b/update-bcd.ts index 4c8b730d5..8640290ef 100644 --- a/update-bcd.ts +++ b/update-bcd.ts @@ -65,6 +65,8 @@ export const findEntry = ( return entry; }; +const clone = (value) => JSON.parse(JSON.stringify(value)); + const combineResults = (results: TestResultValue[]): TestResultValue => { let supported: TestResultValue = null; for (const result of results) { @@ -299,8 +301,9 @@ export const update = ( continue; } + const support = entry.__compat.support; // Stringified then parsed to deep clone the support statements - const originalSupport = JSON.parse(JSON.stringify(entry.__compat.support)); + const originalSupport = clone(support); for (const [browser, versionMap] of browserMap.entries()) { if ( @@ -326,11 +329,21 @@ export const update = ( continue; } - let allStatements: InternalSupportStatement | undefined = - entry.__compat.support[browser]; - if (allStatements === 'mirror') { - allStatements = mirror(browser, originalSupport); - } + // Update the support data with a new value. + const persist = (statements: SimpleSupportStatement[]) => { + support[browser] = statements.length === 1 ? statements[0] : statements; + modified = true; + }; + + let allStatements = + (support[browser] as InternalSupportStatement) === 'mirror' + ? mirror(browser, originalSupport) + : // Although non-mirrored support data could be modified in-place, + // working with a cloned version forces the subsequent code to + // explicitly assign it back to the originating data structure. + // This reduces the likelihood of inconsistencies in the handling + // of mirrored and non-mirrored support data. + clone(support[browser] || null); if (!allStatements) { allStatements = []; @@ -369,11 +382,7 @@ export const update = ( const nonFlagStatements = allStatements.filter( (statement) => !('flags' in statement) ); - entry.__compat.support[browser] = - nonFlagStatements.length === 0 - ? inferredStatement - : [inferredStatement].concat(nonFlagStatements); - modified = true; + persist([inferredStatement, ...nonFlagStatements]); continue; } @@ -439,7 +448,7 @@ export const update = ( ) { simpleStatement.version_added = inferredStatement.version_added.replace('0> ', ''); - modified = true; + persist(allStatements); } } else if ( !( @@ -452,12 +461,12 @@ export const update = ( typeof inferredStatement.version_added === 'string' ? inferredStatement.version_added.replace('0> ', '') : inferredStatement.version_added; - modified = true; + persist(allStatements); } if (typeof inferredStatement.version_removed === 'string') { simpleStatement.version_removed = inferredStatement.version_removed; - modified = true; + persist(allStatements); } } }