From f8d2f9fc5b8ba69752e34ea32f788c9ba2ff3bfc Mon Sep 17 00:00:00 2001 From: Adam Cutchin Date: Wed, 18 Dec 2024 12:13:41 -0500 Subject: [PATCH] Made calculator less awful --- wdio.conf.js | 359 ++++++++++++++++++++++++++++++++++++++------------------------------------------ app/calculator.js | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------ app/calculator.test.js | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 192 insertions(+), 345 deletions(-) diff --git a/wdio.conf.js b/wdio.conf.js index 6bbfdba..f54976d 100644 --- a/wdio.conf.js +++ a/wdio.conf.js @@ -1,295 +1,84 @@ export const config = { - // - // ==================== - // Runner Configuration - // ==================== - // WebdriverIO supports running e2e tests as well as unit and component tests. - runner: 'local', - // - // ================== - // Specify Test Files - // ================== - // Define which test specs should run. The pattern is relative to the directory - // of the configuration file being run. - // - // The specs are defined as an array of spec files (optionally using wildcards - // that will be expanded). The test for each spec file will be run in a separate - // worker process. In order to have a group of spec files run in the same worker - // process simply enclose them in an array within the specs array. - // - // The path of the spec files will be resolved relative from the directory of - // of the config file unless it's absolute. - // - specs: [ - './app/**/*.test.js' - ], - // Patterns to exclude. - exclude: [ - // 'path/to/excluded/files' - ], - // - // ============ - // Capabilities - // ============ - // Define your capabilities here. WebdriverIO can run multiple capabilities at the same - // time. Depending on the number of capabilities, WebdriverIO launches several test - // sessions. Within your capabilities you can overwrite the spec and exclude options in - // order to group specific specs to a specific capability. - // - // First, you can define how many instances should be started at the same time. Let's - // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have - // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec - // files and you set maxInstances to 10, all spec files will get tested at the same time - // and 30 processes will get spawned. The property handles how many capabilities - // from the same test should run tests. - // - maxInstances: 10, - // - // If you have trouble getting all important capabilities together, check out the - // Sauce Labs platform configurator - a great tool to configure your capabilities: - // https://saucelabs.com/platform/platform-configurator - // - capabilities: [{ - // capabilities for local browser web tests - browserName: 'chrome' // or "firefox", "microsoftedge", "safari" - }], + runner: 'local', - // - // =================== - // Test Configurations - // =================== - // Define all options that are relevant for the WebdriverIO instance here - // - // Level of logging verbosity: trace | debug | info | warn | error | silent - logLevel: 'debug', - // - // Set specific log levels per logger - // loggers: - // - webdriver, webdriverio - // - @wdio/browserstack-service, @wdio/lighthouse-service, @wdio/sauce-service - // - @wdio/mocha-framework, @wdio/jasmine-framework - // - @wdio/local-runner - // - @wdio/sumologic-reporter - // - @wdio/cli, @wdio/config, @wdio/utils - // Level of logging verbosity: trace | debug | info | warn | error | silent - // logLevels: { - // webdriver: 'info', - // '@wdio/appium-service': 'info' - // }, - // - // If you only want to run your tests until a specific amount of tests have failed use - // bail (default is 0 - don't bail, run all tests). - bail: 0, - // - // Set a base URL in order to shorten url command calls. If your `url` parameter starts - // with `/`, the base url gets prepended, not including the path portion of your baseUrl. - // If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url - // gets prepended directly. - // baseUrl: 'http://localhost:8080', - // - // Default timeout for all waitFor* commands. - waitforTimeout: 10000, - // - // Default timeout in milliseconds for request - // if browser driver or grid doesn't send response - connectionRetryTimeout: 120000, - // - // Default request retries count - connectionRetryCount: 3, - // - // Test runner services - // Services take over a specific job you don't want to take care of. They enhance - // your test setup with almost no effort. Unlike plugins, they don't add new - // commands. Instead, they hook themselves up into the test process. - // services: [], - // - // Framework you want to run your specs with. - // The following are supported: Mocha, Jasmine, and Cucumber - // see also: https://webdriver.io/docs/frameworks - // - // Make sure you have the wdio adapter package for the specific framework installed - // before running any tests. - framework: 'mocha', + // protocol: 'http', + // hostname: 'selenium-grid-selenium-hub-grid.apps.cluster-mgcjz.dynamic.redhatworkshops.io', + // port: 80, + // path: '/', - // - // The number of times to retry the entire specfile when it fails as a whole - // specFileRetries: 1, - // - // Delay in seconds between the spec file retry attempts - // specFileRetriesDelay: 0, - // - // Whether or not retried spec files should be retried immediately or deferred to the end of the queue - // specFileRetriesDeferred: false, - // - // Test reporter for stdout. - // The only one supported by default is 'dot' - // see also: https://webdriver.io/docs/dot-reporter - reporters: ['spec'], + specs: [ + './app/**/*.test.js' + ], - // Options to be passed to Mocha. - // See the full list at http://mochajs.org/ - mochaOpts: { - ui: 'bdd', - timeout: 60000 - }, + maxInstances: 10, - // - // ===== - // Hooks - // ===== - // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance - // it and to build services around it. You can either apply a single function or an array of - // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got - // resolved to continue. - /** - * Gets executed once before all workers get launched. - * @param {object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - */ - // onPrepare: function (config, capabilities) { - // }, - /** - * Gets executed before a worker process is spawned and can be used to initialize specific service - * for that worker as well as modify runtime environments in an async fashion. - * @param {string} cid capability id (e.g 0-0) - * @param {object} caps object containing capabilities for session that will be spawn in the worker - * @param {object} specs specs to be run in the worker process - * @param {object} args object that will be merged with the main configuration once worker is initialized - * @param {object} execArgv list of string arguments passed to the worker process - */ - // onWorkerStart: function (cid, caps, specs, args, execArgv) { - // }, - /** - * Gets executed just after a worker process has exited. - * @param {string} cid capability id (e.g 0-0) - * @param {number} exitCode 0 - success, 1 - fail - * @param {object} specs specs to be run in the worker process - * @param {number} retries number of retries used - */ - // onWorkerEnd: function (cid, exitCode, specs, retries) { - // }, - /** - * Gets executed just before initialising the webdriver session and test framework. It allows you - * to manipulate configurations depending on the capability or spec. - * @param {object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that are to be run - * @param {string} cid worker id (e.g. 0-0) - */ - // beforeSession: function (config, capabilities, specs, cid) { - // }, - /** - * Gets executed before test execution begins. At this point you can access to all global - * variables like `browser`. It is the perfect place to define custom commands. - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that are to be run - * @param {object} browser instance of created browser/device session - */ - // before: function (capabilities, specs) { - // }, - /** - * Runs before a WebdriverIO command gets executed. - * @param {string} commandName hook command name - * @param {Array} args arguments that command would receive - */ - // beforeCommand: function (commandName, args) { - // }, - /** - * Hook that gets executed before the suite starts - * @param {object} suite suite details - */ - // beforeSuite: function (suite) { - // }, - /** - * Function to be executed before a test (in Mocha/Jasmine) starts. - */ - // beforeTest: function (test, context) { - // }, - /** - * Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling - * beforeEach in Mocha) - */ - // beforeHook: function (test, context, hookName) { - // }, - /** - * Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling - * afterEach in Mocha) - */ - // afterHook: function (test, context, { error, result, duration, passed, retries }, hookName) { + capabilities: [ + { + browserName: 'firefox' + }, + // { + // browserName: 'MicrosoftEdge' // }, - /** - * Function to be executed after a test (in Mocha/Jasmine only) - * @param {object} test test object - * @param {object} context scope object the test was executed with - * @param {Error} result.error error object in case the test fails, otherwise `undefined` - * @param {*} result.result return object of test function - * @param {number} result.duration duration of test - * @param {boolean} result.passed true if test has passed, otherwise false - * @param {object} result.retries information about spec related retries, e.g. `{ attempts: 0, limit: 0 }` - */ - // afterTest: function(test, context, { error, result, duration, passed, retries }) { + // { + // browserName: 'chrome' // or "firefox", "microsoftedge", "safari" // }, + ], + logLevel: 'debug', + // If you only want to run your tests until a specific amount of tests have failed use + // bail (default is 0 - don't bail, run all tests). + bail: 0, + // + // Set a base URL in order to shorten url command calls. If your `url` parameter starts + // with `/`, the base url gets prepended, not including the path portion of your baseUrl. + // If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url + // gets prepended directly. + // baseUrl: 'http://localhost:8080', + // + // Default timeout for all waitFor* commands. + waitforTimeout: 10000, + // + // Default timeout in milliseconds for request + // if browser driver or grid doesn't send response + connectionRetryTimeout: 120000, + // + // Default request retries count + connectionRetryCount: 3, + // + // Test runner services + // Services take over a specific job you don't want to take care of. They enhance + // your test setup with almost no effort. Unlike plugins, they don't add new + // commands. Instead, they hook themselves up into the test process. + // services: [], + // + // Framework you want to run your specs with. + // The following are supported: Mocha, Jasmine, and Cucumber + // see also: https://webdriver.io/docs/frameworks + // + // Make sure you have the wdio adapter package for the specific framework installed + // before running any tests. + framework: 'mocha', - /** - * Hook that gets executed after the suite has ended - * @param {object} suite suite details - */ - // afterSuite: function (suite) { - // }, - /** - * Runs after a WebdriverIO command gets executed - * @param {string} commandName hook command name - * @param {Array} args arguments that command would receive - * @param {number} result 0 - command success, 1 - command error - * @param {object} error error object if any - */ - // afterCommand: function (commandName, args, result, error) { - // }, - /** - * Gets executed after all tests are done. You still have access to all global variables from - * the test. - * @param {number} result 0 - test pass, 1 - test fail - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that ran - */ - // after: function (result, capabilities, specs) { - // }, - /** - * Gets executed right after terminating the webdriver session. - * @param {object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that ran - */ - // afterSession: function (config, capabilities, specs) { - // }, - /** - * Gets executed after all workers got shut down and the process is about to exit. An error - * thrown in the onComplete hook will result in the test run failing. - * @param {object} exitCode 0 - success, 1 - fail - * @param {object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {} results object containing test results - */ - // onComplete: function(exitCode, config, capabilities, results) { - // }, - /** - * Gets executed when a refresh happens. - * @param {string} oldSessionId session ID of the old session - * @param {string} newSessionId session ID of the new session - */ - // onReload: function(oldSessionId, newSessionId) { - // } - /** - * Hook that gets executed before a WebdriverIO assertion happens. - * @param {object} params information about the assertion to be executed - */ - // beforeAssertion: function(params) { - // } - /** - * Hook that gets executed after a WebdriverIO assertion happened. - * @param {object} params information about the assertion that was executed, including its results - */ - // afterAssertion: function(params) { - // } + // + // The number of times to retry the entire specfile when it fails as a whole + // specFileRetries: 1, + // + // Delay in seconds between the spec file retry attempts + // specFileRetriesDelay: 0, + // + // Whether or not retried spec files should be retried immediately or deferred to the end of the queue + // specFileRetriesDeferred: false, + // + // Test reporter for stdout. + // The only one supported by default is 'dot' + // see also: https://webdriver.io/docs/dot-reporter + reporters: ['spec'], + + // Options to be passed to Mocha. + // See the full list at http://mochajs.org/ + mochaOpts: { + ui: 'bdd', + timeout: 60000 + } + } diff --git a/app/calculator.js b/app/calculator.js index cc4f30e..64bbb61 100644 --- a/app/calculator.js +++ a/app/calculator.js @@ -1,7 +1,8 @@ let operand; let operation; let displayString = '0'; +let operationComplete = false; @@ -10,66 +11,81 @@ } function addDigit(digit) { - if (displayString === '0') { + if (displayString === '0' || operationComplete) { displayString = digit; } else { displayString += digit; } + + operationComplete = false; updateDisplay(); } function doAddition() { - operand = parseInt(displayString); - displayString = '0'; - operation = '+'; + doOperation(); + operand = parseInt(displayString); + displayString = '0'; + operation = '+'; } function doSubtraction() { - operand = parseInt(displayString); - displayString = '0'; - operation = '-'; + doOperation(); + operand = parseInt(displayString); + displayString = '0'; + operation = '-'; } function doMultiplication() { - operand = parseInt(displayString); - displayString = '0'; - operation = '*'; + doOperation(); + operand = parseInt(displayString); + displayString = '0'; + operation = '*'; } function doDivision() { - operand = parseInt(displayString); - displayString = '0'; - operation = '/'; + doOperation(); + operand = parseInt(displayString); + displayString = '0'; + operation = '/'; } function doClear() { - operand = undefined; - operation = undefined; - displayString = '0'; - updateDisplay(); + operand = undefined; + operation = undefined; + displayString = '0'; + updateDisplay(); +} + +function doOperation() { + if (operand === undefined || operation === undefined) { + return; + } + + let secondOperand = parseInt(displayString); + if (operation === '+') { + displayString = operand + secondOperand; + } else if (operation === '-') { + displayString = operand - secondOperand; + } else if (operation === '*') { + displayString = operand * secondOperand; + } else if (operation === '/') { + displayString = operand / secondOperand; + } + + operationComplete = true; } function doEquals() { - if (operand === undefined || operation === undefined) { - return; - } - - - let secondOperand = parseInt(displayString); - if (operation === '+') { - displayString = operand + secondOperand; - } else if (operation === '-') { - displayString = operand - secondOperand; - } else if (operation === '*') { - displayString = operand * secondOperand; - } else if (operation === '/') { - displayString = operand / secondOperand; - } - - operand = undefined; - operation = undefined; - updateDisplay(); - displayString = '0'; + if (operand === undefined || operation === undefined) { + return; + } + + + doOperation(); + + operand = undefined; + operation = undefined; + updateDisplay(); } function keyPress(event) { @@ -85,7 +101,7 @@ doDivision(); } else if (event.key === 'Enter') { doEquals(); - } else if (event.key === 'c') { + } else if (event.key === 'c' || event.key === 'Escape') { doClear(); } } diff --git a/app/calculator.test.js b/app/calculator.test.js index 4137d1a..9fb57a2 100644 --- a/app/calculator.test.js +++ a/app/calculator.test.js @@ -1,28 +1,29 @@ import { $, expect } from '@wdio/globals' export class CalcPage { constructor() { - // this.title = 'Calculator'; + this.title = 'Calculator'; } - open() { + open() { return browser.url('http://localhost:8000') + //return browser.url('https://main-grid.apps.cluster-mgcjz.dynamic.redhatworkshops.io') } get doc() { return browser } get display() { return $('#display') } - get button_one() { return $('#digit_1') } - get button_two() { return $('#digit_2') } - get button_three() { return $('#digit_3') } - get button_four() { return $('#digit_4') } - get button_five() { return $('#digit_5') } - get button_six() { return $('#digit_6') } - get button_seven() { return $('#digit_7') } - get button_eight() { return $('#digit_8') } - get button_nine() { return $('#digit_9') } - get button_zero() { return $('#digit_0') } + get button_1() { return $('#digit_1') } + get button_2() { return $('#digit_2') } + get button_3() { return $('#digit_3') } + get button_4() { return $('#digit_4') } + get button_5() { return $('#digit_5') } + get button_6() { return $('#digit_6') } + get button_7() { return $('#digit_7') } + get button_8() { return $('#digit_8') } + get button_9() { return $('#digit_9') } + get button_0() { return $('#digit_0') } get operation_add() { return $('#operator_add') } get operation_multiply() { return $('#operator_multiply') } @@ -30,6 +31,7 @@ get operation_divide() { return $('#operator_divide') } get calculate() { return $('#calculate') } + get clear() { return $('#clear') } } const calcPage = new CalcPage(); @@ -44,31 +46,31 @@ it('Should be able to add 1 + 2', async () => { await calcPage.open(); - await calcPage.button_one.click(); + await calcPage.button_1.click(); await calcPage.operation_add.click(); - await calcPage.button_two.click(); + await calcPage.button_2.click(); await calcPage.calculate.click(); await expect(calcPage.display).toHaveValue('3'); }); it('Should be able to multiply 5 * 15', async () => { await calcPage.open(); - await calcPage.button_five.click(); + await calcPage.button_5.click(); await calcPage.operation_multiply.click(); - await calcPage.button_one.click(); - await calcPage.button_five.click(); + await calcPage.button_1.click(); + await calcPage.button_5.click(); await calcPage.calculate.click(); await expect(calcPage.display).toHaveValue('75'); }); it('Should be able to divide 300 by 25', async () => { await calcPage.open(); - await calcPage.button_three.click(); - await calcPage.button_zero.click(); - await calcPage.button_zero.click(); + await calcPage.button_3.click(); + await calcPage.button_0.click(); + await calcPage.button_0.click(); await calcPage.operation_divide.click(); - await calcPage.button_two.click(); - await calcPage.button_five.click(); + await calcPage.button_2.click(); + await calcPage.button_5.click(); await calcPage.calculate.click(); await expect(calcPage.display).toHaveValue('12'); }); @@ -83,5 +85,45 @@ await calcPage.doc.keys('Enter'); await expect(calcPage.display).toHaveValue('500'); + }); + + it('Should be able to subtract 10 - 5', async () => { + await calcPage.open(); + await calcPage.button_1.click(); + await calcPage.button_0.click(); + await calcPage.operation_subtract.click(); + await calcPage.button_5.click(); + await calcPage.calculate.click(); + await expect(calcPage.display).toHaveValue('5'); + }); + + it(`Should detect each digit being pressed`, async () => { + for (var x = 0; x < 9; x++) { + await calcPage.open(); + await calcPage[`button_${x}`].click(); + await expect(calcPage.display).toHaveValue(`${x}`); + } + }); + + it ('Should support three operations in a row', async () => { + await calcPage.open(); + await calcPage.button_1.click(); + await calcPage.operation_add.click(); + await calcPage.button_2.click(); + await calcPage.operation_multiply.click(); + await calcPage.button_3.click(); + await calcPage.calculate.click(); + await expect(calcPage.display).toHaveValue('9'); + }); + + it('The clear button should work', async () => { + await calcPage.open(); + await calcPage.button_1.click(); + await calcPage.operation_add.click(); + await calcPage.button_2.click(); + await calcPage.calculate.click(); + await expect(calcPage.display).toHaveValue('3'); + await calcPage.clear.click(); + await expect(calcPage.display).toHaveValue('0'); }); }); -- rgit 0.1.4