Skip to content

Commit

Permalink
Merge pull request #512 from adessoSE/CUC-472-Special-Commands
Browse files Browse the repository at this point in the history
CUC-472-Special-Commands
  • Loading branch information
i3rotlher authored Jan 4, 2024
2 parents ba4e31f + cfa4813 commit e7b50d4
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 176 deletions.
180 changes: 18 additions & 162 deletions backend/features/step_definitions/stepdefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require('geckodriver');
const firefox = require('../../node_modules/selenium-webdriver/firefox');
const chrome = require('../../node_modules/selenium-webdriver/chrome');
const edge = require('../../node_modules/selenium-webdriver/edge');
const moment = require('../../node_modules/moment');
const { applySpecialCommands } = require('../../src/serverHelper');

let driver;
const firefoxOptions = new firefox.Options();
Expand Down Expand Up @@ -77,7 +77,7 @@ Before(async function () {
edgeOptions.setMobileEmulation({ deviceName: currentParameters.emulator });
break;
case 'firefox':
// no way to do it ?
// no way to do it ?
}

if (currentParameters.oneDriver) {
Expand Down Expand Up @@ -268,10 +268,10 @@ When('The site should wait for {string} milliseconds', async function (ms) {
When('I insert {string} into the field {string}', async function fillTextField(value, label) {
const world = this;
const identifiers = [`//input[@id='${label}']`, `//input[contains(@id,'${label}')]`, `//textarea[@id='${label}']`, `//textarea[contains(@id,'${label}')]`,
`//textarea[@*='${label}']`, `//textarea[contains(@*='${label}')]`, `//*[@id='${label}']`, `//input[@type='text' and @*='${label}']`,
`//label[contains(text(),'${label}')]/following::input[@type='text']`, `${label}`];
`//textarea[@*='${label}']`, `//textarea[contains(@*='${label}')]`, `//*[@id='${label}']`, `//input[@type='text' and @*='${label}']`,
`//label[contains(text(),'${label}')]/following::input[@type='text']`, `${label}`];

if (value.includes('@@')) value = calcDate(value);
if (value.includes('@@')) value = applyDateCommand(value);

const promises = [];
for (const idString of identifiers) promises.push(
Expand All @@ -293,154 +293,6 @@ When('I insert {string} into the field {string}', async function fillTextField(v
await driver.sleep(100 + currentParameters.waitTime);
});

function calcDate(value) {
// Regex that matches the start: e.g @@Date, @@Day @@Month, @@Day,23
// works only with PCRE2. JS uses EMCAScript
// const start_regex = /^((@@Date)|((@@Day,\d{1,2}|@@Day)|(@@Month,\d{1,2}|@@Month)|(@@Year,\d{4}|@@Year))(?!\1)(((@@Month,\d{1,2}|@@Month)|(@@Year,\d{4}|@@Year)|(@@Day,\d{1,2}|@@Day))(?!\2))?(((@@Year,\d{4}|@@Year)|(@@Day,\d{1,2}|@@Day)|(@@Month,\d{1,2}|@@Month))(?!\3))?)|(^\s*$)/

// Regex that matches the middle: e.g. +@@Day,2-@@Month,4 ....
const mid_regex = /(^((\+|\-)@@(\d+),(Day|Month|Year))*)|(^\s*$)/;
// Regex that matches the format end: e.g @@format:DDMMYY€€
const end_regex = /(^(@@format:\w*€€)*)|(^\s*$)/;

function getStart(str) {
let endIndex = str.length;
const symbols = ['+', '-', '@@format'];

symbols.forEach((symbol) => {
const symbolIndex = str.indexOf(symbol);
if (symbolIndex !== -1 && symbolIndex < endIndex) endIndex = symbolIndex;
});
return str.substring(0, endIndex);
}

function getMid(str) {
let endIndex = str.length;
const symbols = ['@@format'];

symbols.forEach((symbol) => {
const symbolIndex = str.indexOf(symbol);
if (symbolIndex !== -1 && symbolIndex < endIndex) endIndex = symbolIndex;
});
return str.substring(0, endIndex);
}
const start = getStart(value).replace(' ', '');
const mid = getMid(value.replace(start, '')).replace(' ', '');
const end = mid.replace(mid, '').trim();

// check if the start part is written correctly
const dates = start.split(/@@Date/);
const substrings = [/@@Day,\d{1,2}|@@Day/, /@@Month,\d{1,2}|@@Month/, /@@Year,\d{4}|@@Year/];
const substringsErr = ['@@Day', '@@Month', '@@Year'];
// check if @@Date has been used
if (dates.length > 1) if (dates.length - 1 > 1) throw Error('@@Date should only be used once.');
else for (let i = 0; i < substrings.length; i++) {
if (substrings[i].test(start)) throw Error(`@@Date should only be used by itself. Found: ${substringsErr[i]}`);
}

// check the correct usage of @@Day, @@Month, @@Year
else {
startcopy = start.slice();
for (let i = 0; i < substrings.length; i++) {
if (start.split(substrings[i]).length - 1 > 1) throw Error(`${substringsErr[i]} may only be used 0 or 1 time. Input: ${start}`);
startcopy = startcopy.replace(substrings[i], '');
}
// if (startcopy.length !== 0) throw Error(`Unkown tokens in the start section: ${startcopy}`);
}

// check if the calculation part is written correctly
if (!mid_regex.test(mid)) throw Error('Error parsing the calculation section. Example: +@@23,Day-@@Month,1');

// check if the format part is written correctly
if (!end_regex.test(end)) throw Error('Error parsing the format section. Example: @@format:XXXXXX€€. Where XXXXX is the Format String. Example: @@format:DD-MM-YY');

// Get the format e.g @@format:XXXXX€€
let format = value.match(/(@@format:.*€€)/g);

// Start Date
const currDate = new Date();
let day = value.match(/(@@Day,\d{1,2})/g);
if (day) day = parseInt(day[0].match(/@@Day,(\d+)/)[1]);
let month = value.match(/(@@Month,\d{1,2})/g);
if (month) month = parseInt(month[0].match(/@@Month,(\d+)/)[1] - 1);
let year = value.match(/(@@Year,\d\d\d\d)/g);
if (year) year = parseInt(year[0].match(/@@Year,(\d+)/)[1]);

currDate.setFullYear(
year == null ? currDate.getFullYear() : year,
month == null ? currDate.getMonth() : month,
day == null ? currDate.getDate() : day
);

// If no format was found, check the given format e.g. @@Date, @@Day@@Month, @@Day ...
if (format == null) {
// Get the Substring until the first add,sub or format e.g @@Day@@Month+@@ ... -> @@Day@@Month
format = value.split(/[\+\-]/)[0];
// Replace the @@Day, @@Month, @@Year
format = format.replace(/@@Day(,(\d\d){1,2}){0,1}/, 'DD.').replace(/@@Month(,(\d\d){1,2}){0,1}/, 'MM.')
.replace(/@@Year(,(\d\d\d\d)){0,1}/, 'YYYY.')
.replace('@@Date', 'DD.MM.YYYY.')
.slice(0, -1);
} else
// Get @@format: tag and €€ at the end
format = format[0].slice(9, -2);

// console.log(`Day: ${day}\nMonth: ${month}\nYear: ${year}\nFormat: ${format}\nDate: ${currDate.toDateString()}`);

// Get all adds e.g +@@2,Month
let adds = value.match(/\+@@(\d+),(\w+)/g);
// Read values e.g. of +@@5,Day -> {number: 5, kind: "Day"}; or set to empty array if null (no match)
adds = adds ? adds.map((element) => {
const match = element.match(/\+@@(\d+),(\w+)/);
return { number: parseInt(match[1]), kind: match[2] };
}) : [];
// Get all subs e.g -@@10,Year
let subs = value.match(/\-@@(\d+),(\w+)/g);
// Read values e.g. of -@@2,Month -> {number: 2, kind: "Month"}; or set to empty array if null (no match)
subs = subs ? subs.map((element) => {
const match = element.match(/\-@@(\d+),(\w+)/);
return { number: parseInt(match[1]), kind: match[2] };
}) : [];

// Add every add in the adds array
adds.forEach((add) => {
switch (add.kind) {
case 'Day':
currDate.setDate(currDate.getDate() + add.number);
break;
case 'Month':
currDate.setMonth(currDate.getMonth() + add.number);
break;
case 'Year':
currDate.setFullYear(currDate.getFullYear() + add.number);
break;
default:
new Error(`Unknown type to add to the date: ${add.kind}`);
}
});

// Substract every sub in the subs array
subs.forEach((sub) => {
switch (sub.kind) {
case 'Day':
currDate.setDate(currDate.getDate() - sub.number);
break;
case 'Month':
currDate.setMonth(currDate.getMonth() - sub.number);
break;
case 'Year':
currDate.setFullYear(currDate.getFullYear() - sub.number);
break;
default:
new Error(`Unknown type to substract of the date: ${sub.kind}`);
}
});

// Format the date
const result = moment(currDate).format(format);
return result;
}

// "Radio"
When('I select {string} from the selection {string}', async function clickRadioButton(radioname, label) {
const world = this;
Expand Down Expand Up @@ -562,7 +414,7 @@ When('I hover over the element {string} and select the option {string}', async f
});

// TODO:
When('I select from the {string} multiple selection, the values {string}{string}{string}', async () => {});
When('I select from the {string} multiple selection, the values {string}{string}{string}', async () => { });

// Check the Checkbox with a specific name or id
When('I check the box {string}', async function checkBox(name) {
Expand Down Expand Up @@ -699,7 +551,7 @@ Then('So I can see the text {string} in the textbox: {string}', async function c
const world = this;

const identifiers = [`//*[@id='${label}']`, `//*[@*='${label}']`, `//*[contains(@*, '${label}')]`,
`//label[contains(text(),'${label}')]/following::input[@type='text']`, `${label}`];
`//label[contains(text(),'${label}')]/following::input[@type='text']`, `${label}`];
const promises = [];
for (const idString of identifiers) promises.push(driver.wait(until.elementLocated(By.xpath(idString)), searchTimeout, `Timed out after ${searchTimeout} ms`, 100));

Expand Down Expand Up @@ -793,7 +645,8 @@ Then('So the picture {string} has the name {string}', async function checkPictur
const identifiers = [`//picture[source[contains(@srcset, '${picture}')] or img[contains(@src, '${picture}') or contains(@alt, '${picture}') or @id='${picture}' or contains(@title, '${picture}')]]`, `//img[contains(@src, '${picture}') or contains(@alt, '${picture}') or @id='${picture}' or contains(@title, '${picture}')]`, `${picture}`];
const promises = [];
for (const idString of identifiers) promises.push(driver.wait(until.elementLocated(By.xpath(idString)), searchTimeout, `Timed out after ${searchTimeout} ms`, 100));
const domain = (await driver.getCurrentUrl()).split('/').slice(0, 3).join('/');
const domain = (await driver.getCurrentUrl()).split('/').slice(0, 3)
.join('/');
let finSrc = '';
await Promise.any(promises)
.then(async (elem) => {
Expand All @@ -817,7 +670,7 @@ Then('So the picture {string} has the name {string}', async function checkPictur
});
throw Error(e);
});
await fetch(domain + finSrc, { method: 'HEAD' })
await fetch(domain + finSrc, { method: 'HEAD' })
.then((response) => {
if (!response.ok) throw Error(`Image ${finSrc} not Found`);
})
Expand Down Expand Up @@ -883,13 +736,15 @@ Then('So on element {string} the css property {string} is {string}', async funct
await Promise.any(promises)
.then(async (elem) => {
const actual = await elem.getCssValue(property);
if (actual.startsWith('rgba')) {// in selenium colors are always rgba. support.Color is not implemented in javascript
const colorNumbers = actual.replace('rgba(', '').replace(')', '').split(',');
if (actual.startsWith('rgba')) { // in selenium colors are always rgba. support.Color is not implemented in javascript
const colorNumbers = actual.replace('rgba(', '').replace(')', '')
.split(',');
const [r, g, b] = colorNumbers.map((v) => Number(v).toString(16));
const hex = `#${r}${g}${b}`;
expect(value.toString()).to.equal(hex.toString(), `actual ${hex} does not match ${value}`);
} else expect(value.toString()).to.equal(actual.toString(), `actual ${actual} does not match ${value}`);
}).catch(async (e) => {
})
.catch(async (e) => {
await driver.takeScreenshot().then(async (buffer) => {
world.attach(buffer, 'image/png');
});
Expand All @@ -907,14 +762,15 @@ Then('So the element {string} has the tool-tip {string}', async function toolTip
.then(async (elem) => {
const actual = await elem.getAttribute('title');
expect(actual).to.equal(value);
}).catch(async (e) => {
})
.catch(async (e) => {
await driver.takeScreenshot().then(async (buffer) => {
world.attach(buffer, 'image/png');
});
if (Object.keys(e).length === 0) throw NotFoundError(`The Element ${element} could not be found (check tool-tip).`);
throw Error(e);
});
await driver.sleep(100 + currentParameters.waitTime);
await driver.sleep(100 + currentParameters.waitTime);
});

// Closes the webdriver (Browser)
Expand Down
Loading

0 comments on commit e7b50d4

Please sign in to comment.