|
| 1 | +/** |
| 2 | + * Copyright 2017 Google Inc. All rights reserved. |
| 3 | + * Modifications copyright (c) Microsoft Corporation. |
| 4 | + * |
| 5 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | + * you may not use this file except in compliance with the License. |
| 7 | + * You may obtain a copy of the License at |
| 8 | + * |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | + * |
| 11 | + * Unless required by applicable law or agreed to in writing, software |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | + * See the License for the specific language governing permissions and |
| 15 | + * limitations under the License. |
| 16 | + */ |
| 17 | + |
| 18 | +/** |
| 19 | + * @param {Map<string, boolean>} apiCoverage |
| 20 | + * @param {Object} events |
| 21 | + * @param {string} className |
| 22 | + * @param {!Object} classType |
| 23 | + */ |
| 24 | +function traceAPICoverage(apiCoverage, events, className, classType) { |
| 25 | + className = className.substring(0, 1).toLowerCase() + className.substring(1); |
| 26 | + for (const methodName of Reflect.ownKeys(classType.prototype)) { |
| 27 | + const method = Reflect.get(classType.prototype, methodName); |
| 28 | + if (methodName === 'constructor' || typeof methodName !== 'string' || methodName.startsWith('_') || typeof method !== 'function') |
| 29 | + continue; |
| 30 | + apiCoverage.set(`${className}.${methodName}`, false); |
| 31 | + Reflect.set(classType.prototype, methodName, function(...args) { |
| 32 | + apiCoverage.set(`${className}.${methodName}`, true); |
| 33 | + return method.call(this, ...args); |
| 34 | + }); |
| 35 | + } |
| 36 | + |
| 37 | + if (events[classType.name]) { |
| 38 | + for (const event of Object.values(events[classType.name])) { |
| 39 | + if (typeof event !== 'symbol') |
| 40 | + apiCoverage.set(`${className}.emit(${JSON.stringify(event)})`, false); |
| 41 | + } |
| 42 | + const method = Reflect.get(classType.prototype, 'emit'); |
| 43 | + Reflect.set(classType.prototype, 'emit', function(event, ...args) { |
| 44 | + if (typeof event !== 'symbol' && this.listenerCount(event)) |
| 45 | + apiCoverage.set(`${className}.emit(${JSON.stringify(event)})`, true); |
| 46 | + return method.call(this, event, ...args); |
| 47 | + }); |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +module.exports.describe = function({browserType}) { |
| 52 | + describe('**API COVERAGE**', () => { |
| 53 | + const BROWSER_CONFIGS = [ |
| 54 | + { |
| 55 | + name: 'Firefox', |
| 56 | + events: require('../lib/events').Events, |
| 57 | + missingCoverage: ['browserContext.setGeolocation', 'browserContext.setOffline', 'cDPSession.send', 'cDPSession.detach'], |
| 58 | + }, |
| 59 | + { |
| 60 | + name: 'WebKit', |
| 61 | + events: require('../lib/events').Events, |
| 62 | + missingCoverage: ['browserContext.clearPermissions', 'cDPSession.send', 'cDPSession.detach'], |
| 63 | + }, |
| 64 | + { |
| 65 | + name: 'Chromium', |
| 66 | + events: { |
| 67 | + ...require('../lib/events').Events, |
| 68 | + ...require('../lib/chromium/events').Events, |
| 69 | + }, |
| 70 | + missingCoverage: [], |
| 71 | + }, |
| 72 | + ]; |
| 73 | + const browserConfig = BROWSER_CONFIGS.find(config => config.name.toLowerCase() === browserType.name()); |
| 74 | + const events = browserConfig.events; |
| 75 | + const api = require('../lib/api'); |
| 76 | + |
| 77 | + const coverage = new Map(); |
| 78 | + Object.keys(api).forEach(apiName => { |
| 79 | + if (BROWSER_CONFIGS.some(config => apiName.startsWith(config.name)) && !apiName.startsWith(browserConfig.name)) |
| 80 | + return; |
| 81 | + traceAPICoverage(coverage, events, apiName, api[apiName]); |
| 82 | + }); |
| 83 | + |
| 84 | + it('should call all API methods', () => { |
| 85 | + const ignoredMethods = new Set(browserConfig.missingCoverage); |
| 86 | + const missingMethods = []; |
| 87 | + const extraIgnoredMethods = []; |
| 88 | + for (const method of coverage.keys()) { |
| 89 | + // Sometimes we already have a background page while launching, before adding a listener. |
| 90 | + if (method === 'chromiumBrowserContext.emit("backgroundpage")') |
| 91 | + continue; |
| 92 | + if (!coverage.get(method) && !ignoredMethods.has(method)) |
| 93 | + missingMethods.push(method); |
| 94 | + else if (coverage.get(method) && ignoredMethods.has(method)) |
| 95 | + extraIgnoredMethods.push(method); |
| 96 | + } |
| 97 | + if (extraIgnoredMethods.length) |
| 98 | + throw new Error('Certain API Methods are called and should not be ignored: ' + extraIgnoredMethods.join(', ')); |
| 99 | + if (missingMethods.length) |
| 100 | + throw new Error('Certain API Methods are not called: ' + missingMethods.join(', ')); |
| 101 | + }); |
| 102 | + }); |
| 103 | +}; |
0 commit comments