/* global browser */
// imports
import Fragment from './Fragment'
// private property keys
const SEQUENCE_FRAGMENTS = Symbol('Fragments to reference for testing')
const STEPS = Symbol('Steps to perform in the order they are defined')
/**
* Class representing a sequence of tests and actions to perform.
* @example <caption>An example Sequence <a href="https://www.npmjs.com/package/test-automation#example">class</a> from the README. This Sequence creates a Fragment and sets some basic steps to load the Google homepage and check if the image loads.</caption>
* import { GOOGLE_FRAGMENT } from '../constants'
* import { Sequence } from 'test-automation'
* import GoogleFragment from '../fragments/GoogleFragment'
*
* export default class GoogleSequence extends Sequence {
* constructor() {
* super()
*
* this.setFragment(GOOGLE_FRAGMENT, new GoogleFragment())
*
* this.setStep(() => this.getUrl('/'))
* this.setStep(this.getFragment(GOOGLE_FRAGMENT).testElements)
* }
* }
* @example <caption>A slightly more complex example Sequence <a href="https://github.com/fnalabs/test-automation-starter/blob/master/src/sequences/GoogleSequence.js">class</a> from the <a href="https://github.com/fnalabs/test-automation-starter">starter kit</a>. This Sequence creates multiple Fragments and sets many steps to fill out the Google search form, perform a search for `www.google.com`, land on the results page, and go back to the homepage.</caption>
* import { FORM_SELECTOR, INPUT_SELECTOR, LINK_SELECTOR, GOOGLE_FRAGMENT, RESULT_FRAGMENT } from '../constants'
* import { Sequence } from 'test-automation'
*
* import GoogleFragment from '../fragments/GoogleFragment'
* import PageFragment from '../fragments/PageFragment'
* import ResultFragment from '../fragments/ResultFragment'
*
* export default class GoogleSequence extends Sequence {
* constructor () {
* super()
*
* const homepage = this.setFragment(GOOGLE_FRAGMENT, new PageFragment([new GoogleFragment()]))
* const resultpage = this.setFragment(RESULT_FRAGMENT, new ResultFragment())
*
* this.setStep([
* () => this.getUrl('/'),
* () => homepage.testElements(),
* () => homepage.elementSendKeys(INPUT_SELECTOR, 'something'),
* () => homepage.elementClear(INPUT_SELECTOR),
* () => homepage.elementSendKeys(INPUT_SELECTOR, 'www.google.com'),
* () => homepage.elementSubmit(FORM_SELECTOR),
* () => resultpage.testElements(),
* () => resultpage.elementClick(LINK_SELECTOR),
* () => homepage.testElements()
* ])
* }
* }
*/
class Sequence {
constructor () {
this[SEQUENCE_FRAGMENTS] = new Map()
this[STEPS] = []
}
/**
* Gets the Fragment referenced by the key passed.
* @param {string|Symbol} key - The unique key associated with the Fragment.
* @returns {Fragment} The associated fragment.
*/
getFragment (key) {
if (!key || !(/^string|symbol$/.test(typeof key))) throw new TypeError('getFragment(key): key must be a populated String|Symbol')
return this[SEQUENCE_FRAGMENTS].get(key)
}
/**
* Sets the Fragment to be referenced by the key passed.
* @param {string|Symbol} key - The unique key associated with the Fragment.
* @param {Fragment} fragment - The Fragment to associate with the key.
* @returns {Fragment} The associated fragment.
*/
setFragment (key, fragment) {
if (!key || !(/^string|symbol$/.test(typeof key))) throw new TypeError('setFragment(key, fragment): key must be a populated String|Symbol')
if (!(fragment instanceof Fragment)) throw new TypeError('setFragment(key, fragment): fragment must be a Fragment')
this[SEQUENCE_FRAGMENTS].set(key, fragment)
return fragment
}
/**
* Sets a test step to be run, in order, during the test pass.
* @param {Function|Array<Function>} step - The function that wraps a test or action.
*/
setStep (step) {
if (typeof step !== 'function' && !Array.isArray(step)) throw new TypeError('step must be a Function|Array')
Array.isArray(step) ? this[STEPS].push(...step) : this[STEPS].push(step)
}
/**
* [`async`] Getter method that gets the url specified to load in the browser.
* @param {string} url - The relative url path to load.
* @returns {Promise} The promise representing the browser get call.
*/
async getUrl (url) {
if (!url || typeof url !== 'string') throw new TypeError('getUrl(url): url must be a populated String')
return browser.get(url)
}
/**
* [`async`] Method that runs the sequence of test steps previously specified.
* @returns {Promise} The promise chain of test steps.
*/
async runSequence () {
return this[STEPS].reduce((promise, step) => promise.then(step), Promise.resolve())
}
}
export default Sequence