UNPKG

cavy

Version:

An integration test framework for React Native.

186 lines (160 loc) 6.04 kB
// Internal: TestRunner is responsible for actually running through each // suite of tests and executing the specs. // // It also presents the test results and ensures a report is sent to cavy-cli // if necessary. // // component - the Tester component within which the app is wrapped. // testSuites - an array of TestScopes, each of which relate to a single suite // of tests. // startDelay - length of time in ms that cavy should wait before starting // tests. // reporter - the function called with the test report as an argument once // all tests have finished. // export default class TestRunner { constructor(component, testSuites, startDelay, reporter, sendReport, filter) { this.component = component; this.testSuites = testSuites; this.startDelay = startDelay; this.reporter = reporter; // Using the sendReport prop is deprecated - cavy checks whether the // cavy-cli server is listening and sends a report if true. this.shouldSendReport = sendReport; // An array of strings dictating the subset of tagged tests to run, or null // if all tests should be run. this.filter = filter; this.results = []; this.errorCount = 0; } // Internal: Start tests after optional delay time. async run() { if (this.startDelay) { await this.pause(this.startDelay)}; this.runTestSuites(); } // Internal: Synchronously runs each test suite one after the other, // sending a test report to cavy-cli if needed. async runTestSuites() { const start = new Date(); console.log(`Cavy test suite started at ${start}.`); // Iterate through each suite... for (let i = 0; i < this.testSuites.length; i++) { // And then through the suite's test cases... for (let j = 0; j < this.testSuites[i].testCases.length; j++) { let scope = this.testSuites[i]; let tag = scope.testCases[j].tag; // Run the test if: // 1. The test suite isn't filtered // 2. The test suite is filtered and test's tag is included if (!this.filter || (this.filter && this.filter.includes(tag))) { await this.runTest(scope, scope.testCases[j]); }; } } const stop = new Date(); const duration = (stop - start) / 1000; console.log(`Cavy test suite stopped at ${stop}, duration: ${duration} seconds.`); // Handle use of deprecated prop `sendReport` and honour previous expected // behaviour by not reporting results if set to false; if (this.shouldSendReport != undefined) { const message = 'Deprecation warning: using the `sendReport` prop is ' + 'deprecated. By default, Cavy now checks whether the ' + 'cavy-cli server is running and sends a report if a ' + 'connection is detected.' console.warn(message); if (!this.shouldSendReport) return; } const fullResults = { time: duration, timestamp: start, testCases: this.results } // Compile the report object. const report = { results: this.results, fullResults: fullResults, errorCount: this.errorCount, duration: duration } // Send report to reporter (default is cavy-cli) if (this.reporter instanceof Function) { await this.reporter(report); } else if (this.reporter.type == 'realtime') { await this.reporter.onFinish(report); } else if (this.reporter.type == 'deferred') { await this.reporter.send(report); } else { message = 'Could not find a valid reporter. For more ' + 'information on custom reporters, see the documentation ' + 'here: https://cavy.app/docs/guides/writing-custom-reporters'; console.log(message); } } // Internal: Synchronously runs each test case within a test suite, outputting // on the console if the test passes or fails, and adding to testResult // array for reporting purposes. // // Order of actions: // 1. Clears AsyncStorage // 2. Calls a beforeEach function // 3. Re-renders the app // 4. Runs the test async runTest(scope, test) { const start = new Date(); await this.component.clearAsync(); if (scope.beforeEach) { await scope.beforeEach.call(scope) }; this.component.reRender(); const { describeLabel, label, f } = test; const description = `${describeLabel}: ${label}`; // Run the test, console logging the result. try { await f.call(scope); const stop = new Date(); const time = (stop - start) / 1000; let successMsg = `${description} ✅`; console.log(successMsg); this.results.push({ describeLabel: describeLabel, description: description, message: successMsg, passed: true, time: time }); if (!(this.reporter instanceof Function) && this.reporter.type == 'realtime' ) { const result = { message: successMsg, passed: true }; this.reporter.send(result); } } catch (e) { const stop = new Date(); const time = (stop - start) / 1000; let fullErrorMessage = `${description} ❌\n ${e.message}`; console.warn(fullErrorMessage); this.results.push({ describeLabel: describeLabel, description: description, message: fullErrorMessage, errorMessage: e.message, passed: false, time: time }); if (!(this.reporter instanceof Function) && this.reporter.type == 'realtime' ) { const result = { message: fullErrorMessage, passed: false }; this.reporter.send(result); } // Increase error count for reporting. this.errorCount += 1; } } // Internal: Pauses the test runner for a length of time. // Returns a promise. async pause(time) { let promise = new Promise((resolve, reject) => { setTimeout(function() { resolve(); }, time); }); return promise; } }