UNPKG

@kronoslive/codeceptjs

Version:

Supercharged End 2 End Testing Framework for NodeJS

1,384 lines 419 kB
declare namespace CodeceptJS { /** * Helper for managing remote data using REST API. * Uses data generators like [rosie](https://github.com/rosiejs/rosie) or factory girl to create new record. * * By defining a factory you set the rules of how data is generated. * This data will be saved on server via REST API and deleted in the end of a test. * * ## Use Case * * Acceptance tests interact with a websites using UI and real browser. * There is no way to create data for a specific test other than from user interface. * That makes tests slow and fragile. Instead of testing a single feature you need to follow all creation/removal process. * * This helper solves this problem. * Most of web application have API, and it can be used to create and delete test records. * By combining REST API with Factories you can easily create records for tests: * * ```js * I.have('user', { login: 'davert', email: 'davert@mail.com' }); * let id = await I.have('post', { title: 'My first post'}); * I.haveMultiple('comment', 3, {post_id: id}); * ``` * * To make this work you need * * 1. REST API endpoint which allows to perform create / delete requests and * 2. define data generation rules * * ### Setup * * Install [Rosie](https://github.com/rosiejs/rosie) and [Faker](https://www.npmjs.com/package/faker) libraries. * * ```sh * npm i rosie faker --save-dev * ``` * * Create a factory file for a resource. * * See the example for Posts factories: * * ```js * // tests/factories/posts.js * * var Factory = require('rosie').Factory; * var faker = require('faker'); * * module.exports = new Factory() * // no need to set id, it will be set by REST API * .attr('author', () => faker.name.findName()) * .attr('title', () => faker.lorem.sentence()) * .attr('body', () => faker.lorem.paragraph()); * ``` * For more options see [rosie documentation](https://github.com/rosiejs/rosie). * * Then configure ApiDataHelper to match factories and REST API: * ### Configuration * * ApiDataFactory has following config options: * * * `endpoint`: base URL for the API to send requests to. * * `cleanup` (default: true): should inserted records be deleted up after tests * * `factories`: list of defined factories * * `returnId` (default: false): return id instead of a complete response when creating items. * * `headers`: list of headers * * `REST`: configuration for REST requests * * See the example: * * ```js * ApiDataFactory: { * endpoint: "http://user.com/api", * cleanup: true, * headers: { * 'Content-Type': 'application/json', * 'Accept': 'application/json', * }, * factories: { * post: { * uri: "/posts", * factory: "./factories/post", * }, * comment: { * factory: "./factories/comment", * create: { post: "/comments/create" }, * delete: { post: "/comments/delete/{id}" }, * fetchId: (data) => data.result.id * } * } * } * ``` * It is required to set REST API `endpoint` which is the baseURL for all API requests. * Factory file is expected to be passed via `factory` option. * * This Helper uses [REST](http://codecept.io/helpers/REST/) helper and accepts its configuration in "REST" section. * For instance, to set timeout you should add: * * ```js * "ApiDataFactory": { * "REST": { * "timeout": "100000", * } * } * ``` * * ### Requests * * By default to create a record ApiDataFactory will use endpoint and plural factory name: * * * create: `POST {endpoint}/{resource} data` * * delete: `DELETE {endpoint}/{resource}/id` * * Example (`endpoint`: `http://app.com/api`): * * * create: POST request to `http://app.com/api/users` * * delete: DELETE request to `http://app.com/api/users/1` * * This behavior can be configured with following options: * * * `uri`: set different resource uri. Example: `uri: account` => `http://app.com/api/account`. * * `create`: override create options. Expected format: `{ method: uri }`. Example: `{ "post": "/users/create" }` * * `delete`: override delete options. Expected format: `{ method: uri }`. Example: `{ "post": "/users/delete/{id}" }` * * Requests can also be overridden with a function which returns [axois request config](https://github.com/axios/axios#request-config). * * ```js * create: (data) => ({ method: 'post', url: '/posts', data }), * delete: (id) => ({ method: 'delete', url: '/posts', data: { id } }) * * ``` * * Requests can be updated on the fly by using `onRequest` function. For instance, you can pass in current session from a cookie. * * ```js * onRequest: async (request) => { * // using global codeceptjs instance * let cookie = await codeceptjs.container.helpers('WebDriver').grabCookie('session'); * request.headers = { Cookie: `session=${cookie.value}` }; * } * ``` * * ### Responses * * By default `I.have()` returns a promise with a created data: * * ```js * let client = await I.have('client'); * ``` * * Ids of created records are collected and used in the end of a test for the cleanup. * If you need to receive `id` instead of full response enable `returnId` in a helper config: * * ```js * // returnId: false * let clientId = await I.have('client'); * // clientId == 1 * * // returnId: true * let clientId = await I.have('client'); * // client == { name: 'John', email: 'john@snow.com' } * ``` * * By default `id` property of response is taken. This behavior can be changed by setting `fetchId` function in a factory config. * * * ```js * factories: { * post: { * uri: "/posts", * factory: "./factories/post", * fetchId: (data) => data.result.posts[0].id * } * } * ``` * * * ## Methods */ class ApiDataFactory { /** * Generates a new record using factory and saves API request to store it. * * ```js * // create a user * I.have('user'); * // create user with defined email * // and receive it when inside async function * const user = await I.have('user', { email: 'user@user.com'}); * ``` * @param factory - factory to use * @param params - predefined parameters */ have(factory: any, params: any): void; /** * Generates bunch of records and saves multiple API requests to store them. * * ```js * // create 3 posts * I.haveMultiple('post', 3); * * // create 3 posts by one author * I.haveMultiple('post', 3, { author: 'davert' }); * ``` */ haveMultiple(factory: any, times: any, params: any): void; /** * Executes request to create a record in API. * Can be replaced from a in custom helper. */ _requestCreate(factory: any, data: any): void; /** * Executes request to delete a record in API * Can be replaced from a custom helper. */ _requestDelete(factory: any, id: any): void; } /** * Appium Special Methods for Mobile only */ class Appium { /** * Execute code only on iOS * * ```js * I.runOnIOS(() => { * I.click('//UIAApplication[1]/UIAWindow[1]/UIAButton[1]'); * I.see('Hi, IOS', '~welcome'); * }); * ``` * * Additional filter can be applied by checking for capabilities. * For instance, this code will be executed only on iPhone 5s: * * * ```js * I.runOnIOS({deviceName: 'iPhone 5s'},() => { * // ... * }); * ``` * * Also capabilities can be checked by a function. * * ```js * I.runOnAndroid((caps) => { * // caps is current config of desiredCapabiliites * return caps.platformVersion >= 6 * },() => { * // ... * }); * ``` */ runOnIOS(caps: any, fn: any): void; /** * Execute code only on Android * * ```js * I.runOnAndroid(() => { * I.click('io.selendroid.testapp:id/buttonTest'); * }); * ``` * * Additional filter can be applied by checking for capabilities. * For instance, this code will be executed only on Android 6.0: * * * ```js * I.runOnAndroid({platformVersion: '6.0'},() => { * // ... * }); * ``` * * Also capabilities can be checked by a function. * In this case, code will be executed only on Android >= 6. * * ```js * I.runOnAndroid((caps) => { * // caps is current config of desiredCapabiliites * return caps.platformVersion >= 6 * },() => { * // ... * }); * ``` */ runOnAndroid(caps: any, fn: any): void; /** * Check if an app is installed. * * ```js * I.seeAppIsInstalled("com.example.android.apis"); * ``` * @param bundleId - String ID of bundled app * * Appium: support only Android */ seeAppIsInstalled(bundleId: string): void; /** * Check if an app is not installed. * * ```js * I.seeAppIsNotInstalled("com.example.android.apis"); * ``` * @param bundleId - String ID of bundled app * * Appium: support only Android */ seeAppIsNotInstalled(bundleId: string): void; /** * Install an app on device. * * ```js * I.installApp('/path/to/file.apk'); * ``` * @param path - path to apk file * * Appium: support only Android */ installApp(path: string): void; /** * Remove an app from the device. * * ```js * I.removeApp('appName', 'com.example.android.apis'); * ``` * @param bundleId - String ID of bundle * * Appium: support only Android */ removeApp(appId: string, bundleId: string): void; /** * Check current activity on an Android device. * * ```js * I.seeCurrentActivityIs(".HomeScreenActivity") * ``` * @param currentActivity - Appium: support only Android */ seeCurrentActivityIs(currentActivity: string): void; /** * Check whether the device is locked. * * ```js * I.seeDeviceIsLocked(); * ``` * * Appium: support only Android */ seeDeviceIsLocked(): void; /** * Check whether the device is not locked. * * ```js * I.seeDeviceIsUnlocked(); * ``` * * Appium: support only Android */ seeDeviceIsUnlocked(): void; /** * Check the device orientation * * ```js * I.seeOrientationIs('PORTRAIT'); * I.seeOrientationIs('LANDSCAPE') * ``` * @param orientation - LANDSCAPE or PORTRAIT * * Appium: support Android and iOS */ seeOrientationIs(orientation: 'LANDSCAPE' | 'PORTRAIT'): void; /** * Set a device orientation. Will fail, if app will not set orientation * * ```js * I.setOrientation('PORTRAIT'); * I.setOrientation('LANDSCAPE') * ``` * @param orientation - LANDSCAPE or PORTRAIT * * Appium: support Android and iOS */ setOrientation(orientation: 'LANDSCAPE' | 'PORTRAIT'): void; /** * Get list of all available contexts * * ``` * let contexts = await I.grabAllContexts(); * ``` * * Appium: support Android and iOS */ grabAllContexts(): void; /** * Retrieve current context * * ```js * let context = await I.grabContext(); * ``` * * Appium: support Android and iOS */ grabContext(): void; /** * Get current device activity. * * ```js * let activity = await I.grabCurrentActivity(); * ``` * * Appium: support only Android */ grabCurrentActivity(): void; /** * Get information about the current network connection (Data/WIFI/Airplane). * The actual server value will be a number. However WebdriverIO additional * properties to the response object to allow easier assertions. * * ```js * let con = await I.grabNetworkConnection(); * ``` * * Appium: support only Android */ grabNetworkConnection(): void; /** * Get current orientation. * * ```js * let orientation = await I.grabOrientation(); * ``` * * Appium: support Android and iOS */ grabOrientation(): void; /** * Get all the currently specified settings. * * ```js * let settings = await I.grabSettings(); * ``` * * Appium: support Android and iOS */ grabSettings(): void; /** * Switch to the specified context. * @param context - the context to switch to */ _switchToContext(context: any): void; /** * Switches to web context. * If no context is provided switches to the first detected web context * * ```js * // switch to first web context * I.switchToWeb(); * * // or set the context explicitly * I.switchToWeb('WEBVIEW_io.selendroid.testapp'); * ``` */ switchToWeb(context?: string): void; /** * Switches to native context. * By default switches to NATIVE_APP context unless other specified. * * ```js * I.switchToNative(); * * // or set context explicitly * I.switchToNative('SOME_OTHER_CONTEXT'); * ``` */ switchToNative(context: any): void; /** * Start an arbitrary Android activity during a session. * * ```js * I.startActivity('io.selendroid.testapp', '.RegisterUserActivity'); * ``` * * Appium: support only Android */ startActivity(): void; /** * Set network connection mode. * * * airplane mode * * wifi mode * * data data * * ```js * I.setNetworkConnection(0) // airplane mode off, wifi off, data off * I.setNetworkConnection(1) // airplane mode on, wifi off, data off * I.setNetworkConnection(2) // airplane mode off, wifi on, data off * I.setNetworkConnection(4) // airplane mode off, wifi off, data on * I.setNetworkConnection(6) // airplane mode off, wifi on, data on * ``` * See corresponding [webdriverio reference](http://webdriver.io/api/mobile/setNetworkConnection.html). * * Appium: support only Android */ setNetworkConnection(): void; /** * Update the current setting on the device * * ```js * I.setSettings({cyberdelia: 'open'}); * ``` * @param settings - object * * Appium: support Android and iOS */ setSettings(settings: any): void; /** * Hide the keyboard. * * ```js * // taps outside to hide keyboard per default * I.hideDeviceKeyboard(); * I.hideDeviceKeyboard('tapOutside'); * * // or by pressing key * I.hideDeviceKeyboard('pressKey', 'Done'); * ``` * @param strategy - desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’) * * Appium: support Android and iOS */ hideDeviceKeyboard(strategy: 'tapOutside' | 'pressKey'): void; /** * Send a key event to the device. * List of keys: https://developer.android.com/reference/android/view/KeyEvent.html * * ```js * I.sendDeviceKeyEvent(3); * ``` * @param keyValue - Device specific key value * * Appium: support only Android */ sendDeviceKeyEvent(keyValue: number): void; /** * Open the notifications panel on the device. * * ```js * I.openNotifications(); * ``` * * Appium: support only Android */ openNotifications(): void; /** * The Touch Action API provides the basis of all gestures that can be * automated in Appium. At its core is the ability to chain together ad hoc * individual actions, which will then be applied to an element in the * application on the device. * [See complete documentation](http://webdriver.io/api/mobile/touchAction.html) * * ```js * I.makeTouchAction("~buttonStartWebviewCD", 'tap'); * ``` * * Appium: support Android and iOS */ makeTouchAction(): void; /** * Taps on element. * * ```js * I.tap("~buttonStartWebviewCD"); * ``` * * Shortcut for `makeTouchAction` */ tap(locator: any): void; /** * Perform a swipe on the screen. * * ```js * I.performswipe(100,200); * ``` * @param to - Appium: support Android and iOS */ performSwipe(from: number, to: number): void; /** * Perform a swipe down on an element. * * ```js * let locator = "#io.selendroid.testapp:id/LinearLayout1"; * I.swipeDown(locator); // simple swipe * I.swipeDown(locator, 500); // set speed * I.swipeDown(locator, 1200, 1000); // set offset and speed * ``` * @param [yoffset = 1000] - (optional) * @param [speed = 1000] - (optional), 1000 by default * * Appium: support Android and iOS */ swipeDown(locator: CodeceptJS.LocatorOrString, yoffset?: number, speed?: number): void; /** * Perform a swipe left on an element. * * ```js * let locator = "#io.selendroid.testapp:id/LinearLayout1"; * I.swipeLeft(locator); // simple swipe * I.swipeLeft(locator, 500); // set speed * I.swipeLeft(locator, 1200, 1000); // set offset and speed * ``` * @param [xoffset = 1000] - (optional) * @param [speed = 1000] - (optional), 1000 by default * * Appium: support Android and iOS */ swipeLeft(locator: CodeceptJS.LocatorOrString, xoffset?: number, speed?: number): void; /** * Perform a swipe right on an element. * * ```js * let locator = "#io.selendroid.testapp:id/LinearLayout1"; * I.swipeRight(locator); // simple swipe * I.swipeRight(locator, 500); // set speed * I.swipeRight(locator, 1200, 1000); // set offset and speed * ``` * @param [xoffset = 1000] - (optional) * @param [speed = 1000] - (optional), 1000 by default * * Appium: support Android and iOS */ swipeRight(locator: CodeceptJS.LocatorOrString, xoffset?: number, speed?: number): void; /** * Perform a swipe up on an element. * * ```js * let locator = "#io.selendroid.testapp:id/LinearLayout1"; * I.swipeUp(locator); // simple swipe * I.swipeUp(locator, 500); // set speed * I.swipeUp(locator, 1200, 1000); // set offset and speed * ``` * @param [yoffset = 1000] - (optional) * @param [speed = 1000] - (optional), 1000 by default * * Appium: support Android and iOS */ swipeUp(locator: CodeceptJS.LocatorOrString, yoffset?: number, speed?: number): void; /** * Perform a swipe in selected direction on an element to searchable element. * * ```js * I.swipeTo( * "android.widget.CheckBox", // searchable element * "//android.widget.ScrollView/android.widget.LinearLayout", // scroll element * "up", // direction * 30, * 100, * 500); * ``` * @param speed - Appium: support Android and iOS */ swipeTo(searchableLocator: string, scrollLocator: string, direction: string, timeout: number, offset: number, speed: number): void; /** * Performs a specific touch action. * The action object need to contain the action name, x/y coordinates * * ```js * I.touchPerform([{ * action: 'press', * options: { * x: 100, * y: 200 * } * }, {action: 'release'}]) * * I.touchPerform([{ * action: 'tap', * options: { * element: '1', // json web element was queried before * x: 10, // x offset * y: 20, // y offset * count: 1 // number of touches * } * }]); * ``` * * Appium: support Android and iOS */ touchPerform(): void; /** * Pulls a file from the device. * * ```js * I.pullFile('/storage/emulated/0/DCIM/logo.png', 'my/path'); * // save file to output dir * I.pullFile('/storage/emulated/0/DCIM/logo.png', output_dir); * ``` * * Appium: support Android and iOS */ pullFile(): void; /** * Perform a shake action on the device. * * ```js * I.shakeDevice(); * ``` * * Appium: support only iOS */ shakeDevice(): void; /** * Perform a rotation gesture centered on the specified element. * * ```js * I.rotate(120, 120) * ``` * * See corresponding [webdriverio reference](http://webdriver.io/api/mobile/rotate.html). * * Appium: support only iOS */ rotate(): void; /** * Set immediate value in app. * * See corresponding [webdriverio reference](http://webdriver.io/api/mobile/setImmediateValue.html). * * Appium: support only iOS */ setImmediateValue(): void; /** * Simulate Touch ID with either valid (match == true) or invalid (match == false) fingerprint. * * ```js * I.touchId(); // simulates valid fingerprint * I.touchId(true); // simulates valid fingerprint * I.touchId(false); // simulates invalid fingerprint * ``` * * Appium: support only iOS * TODO: not tested */ simulateTouchId(): void; /** * Close the given application. * * ```js * I.closeApp(); * ``` * * Appium: support only iOS */ closeApp(): void; /** * Appends text to a input field or textarea. * Field is located by name, label, CSS or XPath * * ```js * I.appendField('#myTextField', 'appended'); * ``` * @param field - located by label|name|CSS|XPath|strict locator * @param value - text value to append. */ appendField(field: CodeceptJS.LocatorOrString, value: string): void; /** * Selects a checkbox or radio button. * Element is located by label or name or CSS or XPath. * * The second parameter is a context (CSS or XPath locator) to narrow the search. * * ```js * I.checkOption('#agree'); * I.checkOption('I Agree to Terms and Conditions'); * I.checkOption('agree', '//form'); * ``` * @param field - checkbox located by label | name | CSS | XPath | strict locator. * @param [context = null] - (optional, `null` by default) element located by CSS | XPath | strict locator. */ checkOption(field: CodeceptJS.LocatorOrString, context?: CodeceptJS.LocatorOrString): void; /** * Perform a click on a link or a button, given by a locator. * If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string. * For buttons, the "value" attribute, "name" attribute, and inner text are searched. For links, the link text is searched. * For images, the "alt" attribute and inner text of any parent links are searched. * * The second parameter is a context (CSS or XPath locator) to narrow the search. * * ```js * // simple link * I.click('Logout'); * // button of form * I.click('Submit'); * // CSS button * I.click('#form input[type=submit]'); * // XPath * I.click('//form/*[@type=submit]'); * // link in context * I.click('Logout', '#nav'); * // using strict locator * I.click({css: 'nav a.login'}); * ``` * @param locator - clickable link or button located by text, or any element located by CSS|XPath|strict locator. * @param [context = null] - (optional, `null` by default) element to search in CSS|XPath|Strict locator. */ click(locator: CodeceptJS.LocatorOrString, context?: CodeceptJS.LocatorOrString): void; /** * Verifies that the specified checkbox is not checked. * * ```js * I.dontSeeCheckboxIsChecked('#agree'); // located by ID * I.dontSeeCheckboxIsChecked('I agree to terms'); // located by label * I.dontSeeCheckboxIsChecked('agree'); // located by name * ``` * @param field - located by label|name|CSS|XPath|strict locator. */ dontSeeCheckboxIsChecked(field: CodeceptJS.LocatorOrString): void; /** * Opposite to `seeElement`. Checks that element is not visible (or in DOM) * * ```js * I.dontSeeElement('.modal'); // modal is not shown * ``` * @param locator - located by CSS|XPath|Strict locator. */ dontSeeElement(locator: CodeceptJS.LocatorOrString): void; /** * Checks that value of input field or textarea doesn't equal to given value * Opposite to `seeInField`. * * ```js * I.dontSeeInField('email', 'user@user.com'); // field by name * I.dontSeeInField({ css: 'form input.email' }, 'user@user.com'); // field by CSS * ``` * @param field - located by label|name|CSS|XPath|strict locator. * @param value - value to check. */ dontSeeInField(field: CodeceptJS.LocatorOrString, value: string): void; /** * Opposite to `see`. Checks that a text is not present on a page. * Use context parameter to narrow down the search. * * ```js * I.dontSee('Login'); // assume we are already logged in. * I.dontSee('Login', '.nav'); // no login inside .nav element * ``` * @param text - which is not present. * @param [context = null] - (optional) element located by CSS|XPath|strict locator in which to perfrom search. */ dontSee(text: string, context?: CodeceptJS.LocatorOrString): void; /** * Fills a text field or textarea, after clearing its value, with the given string. * Field is located by name, label, CSS, or XPath. * * ```js * // by label * I.fillField('Email', 'hello@world.com'); * // by name * I.fillField('password', secret('123456')); * // by CSS * I.fillField('form#login input[name=username]', 'John'); * // or by strict locator * I.fillField({css: 'form#login input[name=username]'}, 'John'); * ``` * @param field - located by label|name|CSS|XPath|strict locator. * @param value - text value to fill. */ fillField(field: CodeceptJS.LocatorOrString, value: string): void; /** * Retrieves all texts from an element located by CSS or XPath and returns it to test. * Resumes test execution, so **should be used inside async with `await`** operator. * * ```js * let pins = await I.grabTextFromAll('#pin li'); * ``` * @param locator - element located by CSS|XPath|strict locator. * @returns attribute value */ grabTextFromAll(locator: CodeceptJS.LocatorOrString): Promise<string[]>; /** * Retrieves a text from an element located by CSS or XPath and returns it to test. * Resumes test execution, so **should be used inside async with `await`** operator. * * ```js * let pin = await I.grabTextFrom('#pin'); * ``` * If multiple elements found returns first element. * @param locator - element located by CSS|XPath|strict locator. * @returns attribute value */ grabTextFrom(locator: CodeceptJS.LocatorOrString): Promise<string>; /** * Retrieves an array of value from a form located by CSS or XPath and returns it to test. * Resumes test execution, so **should be used inside async function with `await`** operator. * * ```js * let inputs = await I.grabValueFromAll('//form/input'); * ``` * @param locator - field located by label|name|CSS|XPath|strict locator. * @returns attribute value */ grabValueFromAll(locator: CodeceptJS.LocatorOrString): Promise<string[]>; /** * Retrieves a value from a form element located by CSS or XPath and returns it to test. * Resumes test execution, so **should be used inside async function with `await`** operator. * If more than one element is found - value of first element is returned. * * ```js * let email = await I.grabValueFrom('input[name=email]'); * ``` * @param locator - field located by label|name|CSS|XPath|strict locator. * @returns attribute value */ grabValueFrom(locator: CodeceptJS.LocatorOrString): Promise<string>; /** * Scroll element into viewport. * * ```js * I.scrollIntoView('#submit'); * I.scrollIntoView('#submit', true); * I.scrollIntoView('#submit', { behavior: "smooth", block: "center", inline: "center" }); * ``` * @param locator - located by CSS|XPath|strict locator. * @param scrollIntoViewOptions - see https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView. * * * Supported only for web testing */ scrollIntoView(locator: LocatorOrString, scrollIntoViewOptions: ScrollIntoViewOptions): void; /** * Verifies that the specified checkbox is checked. * * ```js * I.seeCheckboxIsChecked('Agree'); * I.seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms * I.seeCheckboxIsChecked({css: '#signup_form input[type=checkbox]'}); * ``` * @param field - located by label|name|CSS|XPath|strict locator. */ seeCheckboxIsChecked(field: CodeceptJS.LocatorOrString): void; /** * Checks that a given Element is visible * Element is located by CSS or XPath. * * ```js * I.seeElement('#modal'); * ``` * @param locator - located by CSS|XPath|strict locator. */ seeElement(locator: CodeceptJS.LocatorOrString): void; /** * Checks that the given input field or textarea equals to given value. * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath. * * ```js * I.seeInField('Username', 'davert'); * I.seeInField({css: 'form textarea'},'Type your comment here'); * I.seeInField('form input[type=hidden]','hidden_value'); * I.seeInField('#searchform input','Search'); * ``` * @param field - located by label|name|CSS|XPath|strict locator. * @param value - value to check. */ seeInField(field: CodeceptJS.LocatorOrString, value: string): void; /** * Checks that a page contains a visible text. * Use context parameter to narrow down the search. * * ```js * I.see('Welcome'); // text welcome on a page * I.see('Welcome', '.content'); // text inside .content div * I.see('Register', {css: 'form.register'}); // use strict locator * ``` * @param text - expected on page. * @param [context = null] - (optional, `null` by default) element located by CSS|Xpath|strict locator in which to search for text. */ see(text: string, context?: CodeceptJS.LocatorOrString): void; /** * Selects an option in a drop-down select. * Field is searched by label | name | CSS | XPath. * Option is selected by visible text or by value. * * ```js * I.selectOption('Choose Plan', 'Monthly'); // select by label * I.selectOption('subscription', 'Monthly'); // match option by text * I.selectOption('subscription', '0'); // or by value * I.selectOption('//form/select[@name=account]','Premium'); * I.selectOption('form select[name=account]', 'Premium'); * I.selectOption({css: 'form select[name=account]'}, 'Premium'); * ``` * * Provide an array for the second argument to select multiple options. * * ```js * I.selectOption('Which OS do you use?', ['Android', 'iOS']); * ``` * @param select - field located by label|name|CSS|XPath|strict locator. * @param option - visible text or value of option. * * * Supported only for web testing */ selectOption(select: LocatorOrString, option: string | any[]): void; /** * Waits for element to be present on page (by default waits for 1sec). * Element can be located by CSS or XPath. * * ```js * I.waitForElement('.btn.continue'); * I.waitForElement('.btn.continue', 5); // wait for 5 secs * ``` * @param locator - element located by CSS|XPath|strict locator. * @param [sec = null] - (optional, `1` by default) time in seconds to wait */ waitForElement(locator: CodeceptJS.LocatorOrString, sec?: number): void; /** * Waits for an element to become visible on a page (by default waits for 1sec). * Element can be located by CSS or XPath. * * ```js * I.waitForVisible('#popup'); * ``` * @param locator - element located by CSS|XPath|strict locator. * @param [sec = 1] - (optional, `1` by default) time in seconds to wait */ waitForVisible(locator: CodeceptJS.LocatorOrString, sec?: number): void; /** * Waits for an element to be removed or become invisible on a page (by default waits for 1sec). * Element can be located by CSS or XPath. * * ```js * I.waitForInvisible('#popup'); * ``` * @param locator - element located by CSS|XPath|strict locator. * @param [sec = 1] - (optional, `1` by default) time in seconds to wait */ waitForInvisible(locator: CodeceptJS.LocatorOrString, sec?: number): void; /** * Waits for a text to appear (by default waits for 1sec). * Element can be located by CSS or XPath. * Narrow down search results by providing context. * * ```js * I.waitForText('Thank you, form has been submitted'); * I.waitForText('Thank you, form has been submitted', 5, '#modal'); * ``` * @param text - to wait for. * @param [sec = 1] - (optional, `1` by default) time in seconds to wait * @param [context = null] - (optional) element located by CSS|XPath|strict locator. */ waitForText(text: string, sec?: number, context?: CodeceptJS.LocatorOrString): void; } /** * Helper for testing filesystem. * Can be easily used to check file structures: * * ```js * I.amInPath('test'); * I.seeFile('codecept.json'); * I.seeInThisFile('FileSystem'); * I.dontSeeInThisFile("WebDriverIO"); * ``` * * ## Methods */ class FileSystem { /** * Enters a directory In local filesystem. * Starts from a current directory */ amInPath(openPath: string): void; /** * Writes test to file */ writeToFile(name: string, text: string): void; /** * Checks that file exists */ seeFile(name: string): void; /** * Waits for file to be present in current directory. * * ```js * I.handleDownloads(); * I.click('Download large File'); * I.amInPath('output/downloads'); * I.waitForFile('largeFilesName.txt', 10); // wait 10 seconds for file * ``` * @param [sec = 1] - seconds to wait */ waitForFile(name: string, sec?: number): void; /** * Checks that file with a name including given text exists in the current directory. * * ```js * I.handleDownloads(); * I.click('Download as PDF'); * I.amInPath('output/downloads'); * I.seeFileNameMatching('.pdf'); * ``` */ seeFileNameMatching(): void; /** * Checks that file found by `seeFile` includes a text. */ seeInThisFile(text: string, encoding?: string): void; /** * Checks that file found by `seeFile` doesn't include text. */ dontSeeInThisFile(text: string, encoding?: string): void; /** * Checks that contents of file found by `seeFile` equal to text. */ seeFileContentsEqual(text: string, encoding?: string): void; /** * Checks that contents of the file found by `seeFile` equal to contents of the file at `pathToReferenceFile`. */ seeFileContentsEqualReferenceFile(pathToReferenceFile: string, encoding?: string, encodingReference?: string): void; /** * Checks that contents of file found by `seeFile` doesn't equal to text. */ dontSeeFileContentsEqual(text: string, encoding?: string): void; /** * Returns file names in current directory. * * ```js * I.handleDownloads(); * I.click('Download Files'); * I.amInPath('output/downloads'); * const downloadedFileNames = I.grabFileNames(); * ``` */ grabFileNames(): void; } function getFileContents(file: string, encoding?: string): string; function isFileExists(file: string, timeout: number): Promise<any>; /** * GraphQL helper allows to send additional requests to a GraphQl endpoint during acceptance tests. * [Axios](https://github.com/axios/axios) library is used to perform requests. * * ## Configuration * * * endpoint: GraphQL base URL * * timeout: timeout for requests in milliseconds. 10000ms by default * * defaultHeaders: a list of default headers * * onRequest: a async function which can update request object. * * ## Example * * ```js * GraphQL: { * endpoint: 'http://site.com/graphql/', * onRequest: (request) => { * request.headers.auth = '123'; * } * } * ``` * * ## Access From Helpers * * Send GraphQL requests by accessing `_executeQuery` method: * * ```js * this.helpers['GraphQL']._executeQuery({ * url, * data, * }); * ``` * * ## Methods */ class GraphQL { /** * Executes query via axios call */ _executeQuery(request: any): void; /** * Prepares request for axios call */ _prepareGraphQLRequest(operation: any, headers: any): void; /** * Send query to GraphQL endpoint over http. * Returns a response as a promise. * * ```js * * const response = await I.sendQuery('{ users { name email }}'); * // with variables * const response = await I.sendQuery( * 'query getUser($id: ID) { user(id: $id) { name email }}', * { id: 1 }, * ) * const user = response.data.data; * ``` * @param variables - that may go along with the query * @param options - are additional query options */ sendQuery(query: string, variables: any, options: any, headers: any): void; /** * Send query to GraphQL endpoint over http * * ```js * I.sendMutation(` * mutation createUser($user: UserInput!) { * createUser(user: $user) { * id * name * email * } * } * `, * { user: { * name: 'John Doe', * email: 'john@xmail.com' * } * }, * }); * ``` * @param variables - that may go along with the mutation * @param options - are additional query options */ sendMutation(mutation: string, variables: any, options: any, headers: any): void; } /** * Helper for managing remote data using GraphQL queries. * Uses data generators like [rosie](https://github.com/rosiejs/rosie) or factory girl to create new record. * * By defining a factory you set the rules of how data is generated. * This data will be saved on server via GraphQL queries and deleted in the end of a test. * * ## Use Case * * Acceptance tests interact with a websites using UI and real browser. * There is no way to create data for a specific test other than from user interface. * That makes tests slow and fragile. Instead of testing a single feature you need to follow all creation/removal process. * * This helper solves this problem. * If a web application has GraphQL support, it can be used to create and delete test records. * By combining GraphQL with Factories you can easily create records for tests: * * ```js * I.mutateData('createUser', { name: 'davert', email: 'davert@mail.com' }); * let user = await I.mutateData('createUser', { name: 'davert'}); * I.mutateMultiple('createPost', 3, {post_id: user.id}); * ``` * * To make this work you need * * 1. GraphQL endpoint which allows to perform create / delete requests and * 2. define data generation rules * * ### Setup * * Install [Rosie](https://github.com/rosiejs/rosie) and [Faker](https://www.npmjs.com/package/faker) libraries. * * ```sh * npm i rosie faker --save-dev * ``` * * Create a factory file for a resource. * * See the example for Users factories: * * ```js * // tests/factories/users.js * * var Factory = require('rosie').Factory; * var faker = require('faker'); * * // Used with a constructor function passed to Factory, so that the final build * // object matches the necessary pattern to be sent as the variables object. * module.exports = new Factory((buildObj) => ({ * input: { ...buildObj }, * })) * // 'attr'-id can be left out depending on the GraphQl resolvers * .attr('name', () => faker.name.findName()) * .attr('email', () => faker.interact.email()) * ``` * For more options see [rosie documentation](https://github.com/rosiejs/rosie). * * Then configure GraphQLDataHelper to match factories and GraphQL schema: * ### Configuration * * GraphQLDataFactory has following config options: * * * `endpoint`: URL for the GraphQL server. * * `cleanup` (default: true): should inserted records be deleted up after tests * * `factories`: list of defined factories * * `headers`: list of headers * * `GraphQL`: configuration for GraphQL requests. * * * See the example: * * ```js * GraphQLDataFactory: { * endpoint: "http://user.com/graphql", * cleanup: true, * headers: { * 'Content-Type': 'application/json', * 'Accept': 'application/json', * }, * factories: { * createUser: { * query: 'mutation createUser($input: UserInput!) { createUser(input: $input) { id name }}', * factory: './factories/users', * revert: (data) => ({ * query: 'mutation deleteUser($id: ID!) { deleteUser(id: $id) }', * variables: { id : data.id}, * }), * }, * } * } * ``` * It is required to set GraphQL `endpoint` which is the URL to which all the queries go to. * Factory file is expected to be passed via `factory` option. * * This Helper uses [GraphQL](http://codecept.io/helpers/GraphQL/) helper and accepts its configuration in "GraphQL" section. * For instance, to set timeout you should add: * * ```js * "GraphQLDataFactory": { * "GraphQL": { * "timeout": "100000", * } * } * ``` * * ### Factory * * Factory contains operations - * * * `operation`: The operation/mutation that needs to be performed for creating a record in the backend. * * Each operation must have the following: * * * `query`: The mutation(query) string. It is expected to use variables to send data with the query. * * `factory`: The path to factory file. The object built by the factory in this file will be passed * as the 'variables' object to go along with the mutation. * * `revert`: A function called with the data returned when an item is created. The object returned by * this function is will be used to later delete the items created. So, make sure RELEVANT DATA IS RETURNED * when a record is created by a mutation. * * ### Requests * * Requests can be updated on the fly by using `onRequest` function. For instance, you can pass in current session from a cookie. * * ```js * onRequest: async (request) => { * // using global codeceptjs instance * let cookie = await codeceptjs.container.helpers('WebDriver').grabCookie('session'); * request.headers = { Cookie: `session=${cookie.value}` }; * } * ``` * * ### Responses * * By default `I.mutateData()` returns a promise with created data as specified in operation query string: * * ```js * let client = await I.mutateData('createClient'); * ``` * * Data of created records are collected and used in the end of a test for the cleanup. * * ## Methods */ class GraphQLDataFactory { /** * Generates a new record using factory, sends a GraphQL mutation to store it. * * ```js * // create a user * I.mutateData('createUser'); * // create user with defined email * // and receive it when inside async function * const user = await I.mutateData('createUser', { email: 'user@user.com'}); * ``` * @param operation - to be performed * @param params - predefined parameters */ mutateData(operation: string, params: any): void; /** * Generates bunch of records and sends multiple GraphQL mutation requests to store them. * * ```js * // crea