UNPKG

cavy

Version:

An integration test framework for React Native.

241 lines (219 loc) 7.71 kB
// Custom error returned when Cavy cannot find the component in the // TestHookStore. class ComponentNotFoundError extends Error { constructor(message) { super(message); this.name = "ComponentNotFoundError"; } } // Custom error returned when a component has not been wrapped, but should have // been. class UnwrappedComponentError extends Error { constructor(message) { super(message); this.name = "UnwrappedComponentError"; } } // Internal: TestScope is responsible for building up testCases to be run by // the TestRunner, and includes all the functions available when writing these // specs. // // component - the Tester component within which the app is wrapped. // waitTime - length of time in ms that cavy should wait before giving up on // finding a component in the testHookStore. export default class TestScope { constructor(component, waitTime) { this.component = component; this.testHooks = component.testHookStore; this.testCases = []; this.waitTime = waitTime; } // Public: Find a component by its test hook identifier. Waits // this.waitTime for the component to appear before abandoning. // // Usually, you'll want to use `exists` instead. // // identifier - String, component identifier registered in the test hook store // via `generateTestHook`. // // Example // // import { assert } from 'assert'; // const c = await spec.findComponent('MyScene.myComponent'); // assert(c, 'Component is missing'); // // Returns a promise; use `await` when calling this function. Resolves the // promise if the component is found, rejects the promise after // this.waitTime if the component is never found in the test hook // store. findComponent(identifier) { let promise = new Promise((resolve, reject) => { let startTime = Date.now(); let loop = setInterval(() => { const component = this.testHooks.get(identifier); if (component) { clearInterval(loop); return resolve(component); } else { if (Date.now() - startTime >= this.waitTime) { reject( new ComponentNotFoundError( `Could not find component with identifier ${identifier}` ) ); clearInterval(loop); } } }, 100); }); return promise; } // Public: Build up a group of test cases. // // label - Label for these test cases. // f - Callback function containing your tests cases defined with `it`. // tag - (Optional) A string tag used to determine whether the group of // tests should run. Defaults to null. // // Example // // // specs/MyFeatureSpec.js // export default function(spec) { // spec.describe('My Scene', function() { // spec.it('Has a component', async function() { // await spec.exists('MyScene.myComponent'); // }, 'focus'); // }); // } // // Returns undefined. describe(label, f, tag = null) { this.describeLabel = label; this.tag = tag; f.call(this); } // Public: Define a test case. // // label - Label for this test case. This is combined with the label from // `describe` when Cavy outputs to the console. // f - The test case. // testTag - (Optional) A string tag used to determine whether the individual // test should run. 'Inherits' a tag from its surrounding describe // block (if present) or defaults to null. // // See example above. it(label, f, testTag = null) { const tag = this.tag || testTag; this.testCases.push({ describeLabel: this.describeLabel, label, f, tag }); } // Public: Runs a function before each test case. // // f - the function to run beforeEach(f) { this.beforeEach = f; } // ACTIONS // Public: Fill in a `TextInput`-compatible component with a string value. // Your component should respond to the property `onChangeText`. // // identifier - Identifier for the component. // str - String to fill in. // // Returns a promise, use await when calling this function. Promise will be // rejected if the component is not found. async fillIn(identifier, str) { const component = await this.findComponent(identifier); component.props.onChangeText(str); } // Public: 'Press' a component (e.g. a `<Button />`). // Your component should respond to the property `onPress`. // // identifier - Identifier for the component. // // Returns a promise, use await when calling this function. Promise will be // rejected if the component is not found. async press(identifier) { const component = await this.findComponent(identifier); component.props.onPress(); } // Public: 'Focus' a component (e.g. a `<TextInput />`). // Your component should respond to the property `onFocus`. // // identifier - Identifier for the component. // // Returns a promise, use await when calling this function. Promise will be // rejected if the component is not found. async focus(identifier) { const component = await this.findComponent(identifier); component.props.onFocus(); } // Public: Pause the test for a specified length of time, perhaps to allow // time for a request response to be received. // // time - Integer length of time to pause for (in milliseconds). // // Returns a promise, use await when calling this function. async pause(time) { let promise = new Promise((resolve, reject) => { setTimeout(function() { resolve(); }, time); }); return promise; } // Public: Checks whether a component e.g. <Text> contains the text string // as a child. // // identifier - Identifier for the component. // text - String // // Returns a promise, use await when calling this function. Promise will be // rejected if the component is not found. async containsText(identifier, text) { const component = await this.findComponent(identifier); if (component.props === undefined) { const msg = "Cannot read property 'children' of undefined.\n" + "Are you using `containsText` with a React <Text> component?\n" + "If so, you need to `wrap` the component first.\n" + "See documentation for Cavy's `wrap` function:" + "https://cavy.app/docs/api/test-hooks#example-3"; throw new UnwrappedComponentError(msg); } const stringifiedChildren = component.props.children.includes ? component.props.children : String(component.props.children); if (!stringifiedChildren.includes(text)) { throw new Error(`Could not find text ${text}`); } } // ASSERTIONS // Public: Check a component exists. // // identifier - Identifier for the component. // // Returns a promise, use await when calling this function. Promise will be // rejected if component is not found, otherwise will be resolved with // `true`. async exists(identifier) { const component = await this.findComponent(identifier); return !!component; } // Public: Check for the absence of a component. Will potentially halt your // test for your maximum wait time. // // identifier - Identifier for the component. // // Returns a promise, use await when calling this function. Promise will be // rejected if the component is not found. async notExists(identifier) { try { await this.findComponent(identifier); } catch (e) { if (e.name == "ComponentNotFoundError") { return true; } throw e; } throw new Error(`Component with identifier ${identifier} was present`); } }