diff --git a/bookshop/app/vue/index.html b/bookshop/app/vue/index.html index c8e23209..e18eabe7 100644 --- a/bookshop/app/vue/index.html +++ b/bookshop/app/vue/index.html @@ -44,11 +44,11 @@

{{ document.title }}

- +

{{ book.title }}

diff --git a/package.json b/package.json index 9c5c6c34..0bf966db 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "chai-subset": "^1.6.0", + "@cucumber/cucumber": "^7.0.0", + "selenium-webdriver": "^4.0.0-beta.1", "sqlite3": "5.0.0" }, "scripts": { diff --git a/test/cds.ql.test.js b/test/cds.ql.test.js index a8bfa7be..dfade6b0 100644 --- a/test/cds.ql.test.js +++ b/test/cds.ql.test.js @@ -365,7 +365,7 @@ describe('cds.ql → cqn', () => { }, }) - expect( + if (!is_v2) expect( SELECT.from(Foo).where(`x=`, 1, `or y.z is null and (a>`, 2, `or b=`, 3, `)`) ).to.eql(CQL`SELECT from Foo where x=1 or y.z is null and (a>2 or b=3)`) diff --git a/test/features/bookshop.feature b/test/features/bookshop.feature new file mode 100644 index 00000000..ab3288b7 --- /dev/null +++ b/test/features/bookshop.feature @@ -0,0 +1,34 @@ +Feature: List Books using Vue.js UI + + Scenario: Launch cds server for bookshop + When we run the 'bookshop' server + And wait for 1s + Then it should listen at 'http://localhost:4004' + + Scenario: Display Books List + When we open page '/vue/index.html' + And wait for 1s + Then it should list these rows in table 'books': + | Wuthering Heights | Emily Brontë | + | Jane Eyre | Charlotte Brontë | + | The Raven | Edgar Allen Poe | + | Eleonora | Edgar Allen Poe | + | Catweazle | Richard Carpenter | + + Scenario: Select a Book + When we click on the 1st row in table 'books' + Then it shows '12' in 'stock' + + Scenario: Order One Book + When we click on button 'Order:' + Then it succeeds with 'ordered 1 item(s)' + + Scenario: Order Four Books + When we enter '4' into 'amount' + And we click on button 'Order:' + Then it succeeds with 'ordered 4 item(s)' + + Scenario: Order Amount Exceeding Stock + When we enter '9' into 'amount' + And we click on button 'Order:' + Then it fails with '9 exceeds stock' diff --git a/test/features/step_definitions/bookshop_steps.js b/test/features/step_definitions/bookshop_steps.js new file mode 100644 index 00000000..131eb9a6 --- /dev/null +++ b/test/features/step_definitions/bookshop_steps.js @@ -0,0 +1,82 @@ +const { Given, When, Then, AfterAll } = require('@cucumber/cucumber') +const { Builder, By } = require('selenium-webdriver') +const browser = (new Builder).forBrowser('safari').build() +const cds = require ('@sap/cds/lib') + +process.env.cds_requires_auth_strategy = 'dummy' + +let axios = require('axios').default +let {display} = browser + +When('we run the {string} server', project => cds.exec('watch', project)) +Then('it should listen at {string}', baseURL => { + axios = axios.create({ baseURL }) + display = url => browser.get (baseURL+url) + return axios.head() +}) +Then('terminate the server', ()=> process.exit()) + + + +When(/wait for (\d+)\s*(\w+)/, {timeout:60*1000}, (delay, unit, done) => { + const factor = { + ms: 1, + s: 1000, sec: 1000, second: 1000, seconds: 1000, + m: 60*1000, min: 60*1000, minute: 60*1000, minutes: 60*1000, + h: 60*60*1000, hr: 60*60*1000, hour: 60*60*1000, hours: 60*60*1000, + }[unit] + if (!factor) throw `Unknown duration unit: ${unit}` + setTimeout (done, delay * factor) +}) + +AfterAll(()=> setTimeout (process.exit, 111)) + + + + + +When('we open page {string}', page => display(page)) + +Then('it should list these rows in table {string}:', async (id,data) => { + let rows = await browser.findElements(By.css(`#${id} tr`)); rows.shift() + await Promise.all (data.rawTable.map (async (row,i)=>{ + const tr = await rows[i].getText() + for (let each of row) if (!tr.match(each)) throw `Didn't find '${each}' in web page as expected` + })) +}) + +When(/we click on the (\d+)(?:st|nd|rd|th) row in table '(\w+)'/, async (row, id) => { + let rows = await browser.findElements(By.css(`#${id} tr`)) + let td = await rows[row].findElement(By.css('td')) + return td.click() +}) + +When('we enter {string} into {string}', async (value,id) => { + const field = await browser.findElement(By.css(`input#${id}`)) + return field.sendKeys('\b\b\b\b\b\b',value) +}) + +When('we click on button {string}', async (text) => { + const button = await browser.findElement(By.css(`input[value='${text}']`)) + return button.click() +}) + +Then('it succeeds with {string}', async message => { + const element = await browser.wait (browser.findElement(By.css(`span.succeeded`))) + return (await element.getText()).includes(message) +}) + +Then('it fails with {string}', async message => { + const element = await browser.wait (browser.findElement(By.css(`span.failed`))) + return (await element.getText()).includes(message) +}) + +Then('it shows {string} in {string}', async (message,id) => { + const element = await browser.wait (browser.findElement(By.id(id))) + return (await element.getText()).includes(message) +}) + +Given('we login as {string}, {string}', async (username, password) => { + const alert = await browser.switchTo().alert() + return alert.authenticateAs(username, password) +})