Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CUC-472-Special-Commands #510

Merged
merged 14 commits into from
Jan 4, 2024
6 changes: 3 additions & 3 deletions .github/workflows/CI_Tests_and_Report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,14 @@ jobs:
- name: Send sanity POST request and store response
env:
# <-- api url for sanity test -->/<---- repository_id --->/<------ group_id ------>
API_URL: localhost:8080/api/sanity/test/632893aad3bd45536c41b684/6400a70f5eda22409c4d2ed9
API_URL: localhost:8080/api/sanity/test/${{ secrets.SANITY_REPO_ID }}/${{ secrets.SANITY_GROUP_ID }}
run: |
curl -X POST -H 'Content-Type: application/json' -d '{"email": "${{ secrets.SEED_EMAIL }}", "password": "${{ secrets.SEED_PW }}", "stayLoggedIn": true, "repository": "Seed-Test", "source": "db"}' "$API_URL" > sanityReport.txt
- name: print sanity
run: |
echo $(cat sanityReport.txt)
- name: Get passed and total sanity
run: |
passed=$(awk '/Steps:/ { match($0, /[0-9]+ passed/); print substr($0, RSTART, RLENGTH-7) }' sanityReport.txt)
Expand Down Expand Up @@ -193,4 +193,4 @@ jobs:
sanityStepsFailed: ${{ steps.passed-total-sanity.outputs.failedSteps }}
sanityStepsSkipped: ${{ steps.passed-total-sanity.outputs.skippedSteps }}
workflowLink: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
webhook: ${{ secrets.MS_TEAMS_WEBHOOK_URI }}
webhook: ${{ secrets.MS_TEAMS_WEBHOOK_URI }}
182 changes: 19 additions & 163 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 All @@ -465,7 +317,7 @@ When('I select {string} from the selection {string}', async function clickRadioB
When('I select the option {string} from the drop-down-menue {string}', async (value, dropd) => {
let world;
const identifiers = [`//*[@*='${dropd}']/option[text()='${value}']`, `//label[contains(text(),'${dropd}')]/following::button[text()='${value}']`,
`//label[contains(text(),'${dropd}')]/following::span[text()='${value}']`, `//*[contains(text(),'${dropd}')]/following::*[contains(text(),'${value}']`, `${dropd}`];
`//label[contains(text(),'${dropd}')]/following::span[text()='${value}']`, `//*[contains(text(),'${dropd}')]/following::*[contains(text(),'${value}']`, `${dropd}`];
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 @@ -540,7 +392,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 @@ -668,7 +520,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 @@ -761,7 +613,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 @@ -785,7 +638,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 @@ -850,13 +703,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 @@ -874,14 +729,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
Loading