How to add a Before() function in Cypress Feature Files? - cucumber

I am using feature files in my Cypress framework.
Below is an example of a scenario:
Scenario: #1 Cancel should return to Customer Management landing page
Given User is on the Edit Customer Page
When User updates the email address to "abc#gmail.com"
Then the updated email address will appear on the summary page
The problem I'm facing is that when I re-run this test, the original email address will be "abc#gmail.com" (the value it was updated to during the first test run). So the test won't actually update the email address.
What is the best approach to deal with this issue?
I was thinking of using something like a Before() function to delete the customer if it exists, & re-create it. Then I'd be able to update it & the values will be the same each time.
However, I don't know how to add a Before() in a feature file. I have a Background that navigates to a certain page, but is that the place where I should be putting it?

The cypress-cucumber-preprocessor supports both Mocha's before/beforeEach/after/afterEach hooks and Cucumber's Before and After hooks. So in your step Definition file, you can:
//This example is with the Cucumber hooks
const {
Before,
After,
Given,
Then,
} = require('cypress-cucumber-preprocessor/steps')
// this will get called before each scenario
Before(() => {
//Code to delete customer if it exists
})

TheBrainFamily recommend using Given
Please see Step definitions
This is a good place to put before/beforeEach/after/afterEach hooks related to that particular feature. This is incredibly hard to get right with pure cucumber.
So
Add codition to Given
Use beforeEach()
import { Given } from "cypress-cucumber-preprocessor/steps";
const url = 'https://google.com'
Given('User is on the Edit Customer Page And Email is empty', () => {
beforeEach(() => {
// reset email
})
...
})

Related

Why is my dynamic Gatsby page not working

I'm trying to create dynamic pages based on a database that grows by the minute. Therefor it isn't an option to use createPage and build several times a day.
I'm using onCreatePage here to create pages which works fine for my first route, but when I try to make an English route somehow it doesn't work.
gatby-node.js:
exports.onCreatePage = async ({ page, actions: { createPage } }) => {
if (page.path.match(/^\/listing/)) {
page.matchPath = '/listing/:id'
createPage(page)
}
if (page.path.match(/^\/en\/listing/)) {
page.matchPath = '/en/listing/:id'
createPage(page)
}
}
What I'm trying to achieve here is getting 2 dynamic routes like:
localhost:8000/listing/123 (this one works)
localhost:8000/en/listing/123 (this one doesn't work)
My pages folder looks like this:
pages
---listing.tsx
---en/
------listing.tsx
Can anyone see what I'm doing wrong here?
--
P.S. I want to use SSR (available since Gatsby v4) by using the getServerData() in the templates for these pages. Will that work together with pages created dynamically with onCreatePage or is there a better approach?
According to what we've discussed in the comment section: the fact that the /en/ path is never created, hence is not entering the following condition:
if (page.path.match(/^\/en\/listing/)) {
page.matchPath = '/en/listing/:id'
createPage(page)
}
Points me to think that the issue is on your createPages API rather than onCreatePage, which means that your english page is not even created.
Keep in mind that onCreatePage API is a callback called when a page is created, so it's triggered after createPages.
If you add a console.log(page.path) you shouldn't see the English page in the IDE/text editor console so try debugging how are you creating the /en/ route because it seems that onCreatePage doesn't have any problem.

Context Management in Bot Framework - Node JS

in the main dialog, I have used this to get the details of the user activity and session ID stepContext.context._activity
and I am able to store the data in JSON format,
I want to know how to use the previous text of the user for the next query if there is no entity present in that and it should work for multiple users, not for a single user.
The location of the user's input varies depending on the option provided to them. Simple text input is available in the stepContext.context.activity.text field. However, the user's returned value could, for example, exist in:
stepContext.result, if a hero card is used
stepContext.options, if a value is forwarded on from a previous step or action
stepContext.context.activity.text, if a suggested action is used
stepContext.context.activity.value, if a postBack is returned
There are a few other possibilities. Referencing the docs is a good place to start as are the BotBuilder-Samples for getting a grasp on all the possibilities. Don't be averse to inspecting the stepContext to see how it is populated and with what.
async firstStep(stepContext) {
await stepContext.context.sendActivity('Type something...');
return { status: DialogTurnStatus.waiting };
}
async secondStep(stepContext) {
const result = stepContext.context.activity.text;
return await stepContext.context.sendActivity(`You said: ${ result }`);
Hope of help!

Strapi & react-admin : I'd like to set 'Content-Range' header dynamically when any fetchAll query fires

I'm still a novice web developer, so please bear with me if I miss something fundamental !
I'm creating a backoffice for a Strapi backend, using react-admin.
React-admin library uses a 'data provider' to link itself with an API. Luckily someone already wrote a data provider for Strapi. I had no problem with step 1 and 2 of this README, and I can authenticate to Strapi within my React app.
I now want to fetch and display my Strapi data, starting with Users. In order to do that, quoting Step 3 of this readme : 'In controllers I need to set the Content-Range header with the total number of results to build the pagination'.
So far I tried to do this in my User controller, with no success.
What I try to achieve:
First, I'd like it to simply work with the ctx.set('Content-Range', ...) hard-coded in the controller like aforementioned Step 3.
Second, I've thought it would be very dirty to c/p this logic in every controller (not to mention in any future controllers), instead of having some callback function dynamically appending the Content-Range header to any fetchAll request. Ultimately that's what I aim for, because with ~40 Strapi objects to administrate already and plenty more to come, it has to scale.
Technical infos
node -v: 11.13.0
npm -v: 6.7.0
strapi version: 3.0.0-alpha.25.2
uname -r output: Linux 4.14.106-97.85.amzn2.x86_64
DB: mySQL v2.16
So far I've tried accessing the count() method of User model like aforementioned step3, but my controller doesn't look like the example as I'm working with users-permissions plugin.
This is the action I've tried to edit (located in project/plugins/users-permissions/controllers/User.js)
find: async (ctx) => {
let data = await strapi.plugins['users-permissions'].services.user.fetchAll(ctx.query);
data.reduce((acc, user) => {
acc.push(_.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken']));
return acc;
}, []);
// Send 200 `ok`
ctx.send(data);
},
From what I've gathered on Strapi documentation (here and also here), context is a sort of wrapper object. I only worked with Express-generated APIs before, so I understood this snippet as 'use fetchAll method of the User model object, with ctx.query as an argument', but I had no luck logging this ctx.query. And as I can't log stuff, I'm kinda blocked.
In my exploration, I naively tried to log the full ctx object and work from there:
// Send 200 `ok`
ctx.send(data);
strapi.log.info(ctx.query, ' were query');
strapi.log.info(ctx.request, 'were request');
strapi.log.info(ctx.response, 'were response');
strapi.log.info(ctx.res, 'were res');
strapi.log.info(ctx.req, 'were req');
strapi.log.info(ctx, 'is full context')
},
Unfortunately, I fear I miss something obvious, as it gives me no input at all. Making a fetchAll request from my React app with these console.logs print this in my terminal:
[2019-09-19T12:43:03.409Z] info were query
[2019-09-19T12:43:03.410Z] info were request
[2019-09-19T12:43:03.418Z] info were response
[2019-09-19T12:43:03.419Z] info were res
[2019-09-19T12:43:03.419Z] info were req
[2019-09-19T12:43:03.419Z] info is full context
[2019-09-19T12:43:03.435Z] debug GET /users?_sort=id:DESC&_start=0&_limit=10& (74 ms)
While in my frontend I get the good ol' The Content-Range header is missing in the HTTP Response message I'm trying to solve.
After writing this wall of text I realize the logging issue is separated from my original problem, but if I was able to at least log ctx properly, maybe I'd be able to find the solution myself.
Trying to summarize:
Actual problem is, how do I set my Content-Range properly in my strapi controller ? (partially answered cf. edit 3)
Collateral problem n°1: Can't even log ctx object (cf. edit 2)
Collateral problem n°2: Once I figure out the actual problem, is it feasible to address it dynamically (basically some callback function for index/fetchAll routes, in which the model is a variable, on which I'd call the appropriate count() method, and finally append the result to my response header)? I'm not asking for the code here, just if you think it's feasible and/or know a more elegant way.
Thank you for reading through and excuse me if it was confuse; I wasn't sure which infos would be relevant, so I thought the more the better.
/edit1: forgot to mention, in my controller I also tried to log strapi.plugins['users-permissions'].services.user object to see if it actually has a count() method but got no luck with that either. Also tried the original snippet (Step 3 of aforementioned README), but failed as expected as afaik I don't see the User model being imported anywhere (the only import in User.js being lodash)
/edit2: About the logs, my bad, I just misunderstood the documentation. I now do:
ctx.send(data);
strapi.log.info('ctx should be : ', {ctx});
strapi.log.info('ctx.req = ', {...ctx.req});
strapi.log.info('ctx.res = ', {...ctx.res});
strapi.log.info('ctx.request = ', {...ctx.request});
ctrapi.log.info('ctx.response = ', {...ctx.response});
Ctx logs this way; also it seems that it needs the spread operator to display nested objects ({ctx.req} crash the server, {...ctx.req} is okay). Cool, because it narrows the question to what's interesting.
/edit3: As expected, having logs helps big time. I've managed to display my users (although in the dirty way). Couldn't find any count() method, but watching the data object that is passed to ctx.send(), it's equivalent to your typical 'res.data' i.e a pure JSON with my user list. So a simple .length did the trick:
let data = await strapi.plugins['users-permissions'].services.user.fetchAll(ctx.query);
data.reduce((acc, user) => {
acc.push(_.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken']));
return acc;
}, []);
ctx.set('Content-Range', data.length) // <-- it did the trick
// Send 200 `ok`
ctx.send(data);
Now starting to work on the hard part: the dynamic callback function that will do that for any index/fetchAll call. Will update once I figure it out
I'm using React Admin and Strapi together and installed ra-strapi-provider.
A little boring to paste Content-Range header into all of my controllers, so I searched for a better solution. Then I've found middleware concept and created one that fits my needs. It's probably not the best solution, but do its job well:
const _ = require("lodash");
module.exports = strapi => {
return {
// can also be async
initialize() {
strapi.app.use(async (ctx, next) => {
await next();
if (_.isArray(ctx.response.body))
ctx.set("Content-Range", ctx.response.body.length);
});
}
};
};
I hope it helps
For people still landing on this page:
Strapi has been updated from #alpha to #beta. Care, as some of the code in my OP is no longer valid; also some of their documentation is not up to date.
I failed to find a "clever" way to solve this problem; in the end I copy/pasted the ctx.set('Content-Range', data.length) bit in all relevant controllers and it just worked.
If somebody comes with a clever solution for that problem I'll happily accept his answer. With the current Strapi version I don't think it's doable with policies or lifecycle callbacks.
The "quick & easy fix" is still to customize each relevant Strapi controller.
With strapi#beta you don't have direct access to controller's code: you'll first need to "rewrite" one with the help of this doc. Then add the ctx.set('Content-Range', data.length) bit. Test it properly with RA, so for the other controllers, you'll just have to create the folder, name the file, copy/paste your code + "Search & Replace" on model name.
The "longer & cleaner fix" would be to dive into the react-admin source code and refactorize so the lack of "Content-Range" header doesn't break pagination.
You'll now have to maintain your own react-admin fork, so make sure you're already committed into this library and have A LOT of tables to manage through it (so much that customizing every Strapi controller will be too tedious).
Before forking RA, please remember all the stuff you can do with the Strapi backoffice alone (including embedding your custom React app into it) and ensure it will be worth the trouble.

Page Object Pattern in Cypress?

I want to use a workflow similar to the Page Object Pattern that exists in frameworks like Selenium. I want to use my login.spec.js in my editSettings.spec.js, because it requires a user to be logged in.
How do I achieve this in Cypress? Can I export a function from one test file to use in another?
Yes, Cypress supports the ability to create and reuse actions in your UI, such as logging in as a user would.
However, Cypress also allows you to control the state of the browser more powerfully than a user would.
For example: I create a test that a "user can log in with valid username and password"- Cypress navigates to the login page, types in the user field, types in the password field and clicks the "Log in" button. The Page Object Pattern would have you reuse this action on every test that requires a user to be logged in (most of the tests)
Cypress supports this; however, this is slower than it has to be. It takes a considerable amount of time to navigate to a login page, type in the information, handle the response, and navigate to the page under test.
Instead, Cypress's API allows the following:
use cy.request() to directly hit your server with the login credentials. This requires no state of your app, no typing in fields, no clicking buttons, or page directs
Any cookies your site uses are automatically set, or you can set localStorage using the response
Make this a custom command, call it before every test, and boom- you've generated your user's state almost instantly and most importantly flake-free
I actually came up with these two examples, one using JavaScript and another one with Typescript.
https://github.com/antonyfuentes/cypress-typescript-page-objects
https://github.com/antonyfuentes/cypress-javascript-page-objects
Hopefully, this helps someone else.
Create a SearchProduct.js file in fixtures folder (You can create it anywhere).Then create a class in it and define your all the methods in it something like this:
class ProductPage {
getSearchClick() {
return cy.get('.noo-search');
}
getSearchTextBox(){
return cy.get('.form-control');
}
getProductsName() {
return cy.get('.noo-product-inner h3');
}
getSelectSize() {
return cy.get('#pa_size');
}
getSelectColor() {
return cy.get('#pa_color');
}
getAddtoCartButton() {
return cy.get('.single_add_to_cart_button');
}
}
export default ProductPage
After creating the class, let's import it in the command.js file. After that, let's create a new object of it to access all the methods mentioned above in commands.js.
import ProductPage from '../support/PageObjects/ProductPage';
Cypress.Commands.add("selectProduct", (productName, size , color) => {
// Creating Object for ProductPage
const productPage=new ProductPage();
// Doing the search part for Shirts.
productPage.getSearchClick().click()
productPage.getSearchTextBox().type('Shirt');
productPage.getSearchTextBox().type('{enter}')
productPage.getProductsName().each(($el , index , $list) => {
//cy.log($el.text());
if($el.text().includes(productName)) {
cy.get($el).click();
}
})
// Selecting the size and color and then adding to cart button.
productPage.getSelectColor().select(color);
productPage.getSelectSize().select(size);
productPage.getAddtoCartButton().click();
})
So, here actually the custom command's class is importing and using the Page class. Additionally, the test script will use the same command.js to perform the needed action.
So, the test script will still be the same and will look as below:
// type definitions for Cypress object "cy"
// <reference types="cypress" />
describe('Cypress Page Objects and Custom Commands', function() {
//Mostly used for Setup Part
before(function(){
cy.fixture('example').then(function(data)
{
this.data=data ;
})
})
it('Cypress Test Case', function() {
//Registration on the site
cy.visit('https://shop.demoqa.com/my-account/');
cy.get('#reg_username').type(this.data.Username);
cy.get('#reg_email').type(this.data.Email);
cy.get('#reg_password').type(this.data.NewPassword);
cy.get('.woocommerce-Button').click();
//Checking whether the Registration is successful and whether UserName is populated under login section
cy.get('#username').should('have.value',this.data.Username);
})
// For Loop for Accessing productName array from Features File and Using the custom command
this.data.productName.forEach(function(element){
// Invoke the Custom command selectProduct
cy.selectProduct(element[0],element[1],element[2]);
})
})
You can also directly import the class in Test File by skipping Command.js file.
For that go to the following link:
Courtesy: https://softans.com/cypress-page-object-model/

Frisby Functional Test standards

I'm new to this and I have been searching for ways (or standards) to write proper functional tests but I still I have many unanswered questions. I'm using FrisbyJS to write functional tests for my NodeJS API application and jasmine-node to run them.
I have gone through Frisby's documentation, but it wasn't fruitful for me.
Here is a scenario:
A guest can create a User. (No username duplication allowed, obviously)
After creating a User, he can login. On successful login, he gets an Access-Token.
A User can create a Post. Then a Post can have Comment, and so on...
A User cannot be deleted once created. (Not from my NodeJS Application)
What Frisby documentation says is, I should write a test within a test.
For example (full-test.spec.js):
// Create User Test
frisby.create('Create a `User`')
.post('http://localhost/users', { ... }, {json: true})
.expectStatus(200)
.afterJSON(function (json) {
// User Login Test
frisby.create('Login `User`')
.post('http://localhost/users/login', { ... }, {json: true})
.expectStatus(200)
.afterJSON(function (json) {
// Another Test (For example, Create a post, and then comment)
})
.toss();
})
.toss();
Is this the right way to write a functional test? I don't think so... It looks dirty.
I want my tests to be modular. Separate files for each test.
If I create separate files for each test, then while writing a test for Create Post, I'll need a User's Access-Token.
To summarize, the question is: How should I write tests if things are dependent on each other?
Comment is dependent on Post. Post is dependent on User.
This is the by product to using NodeJS. This is a large reason I regret deciding on frisby. That and the fact I can't find a good way to load expected results out of a database in time to use them in the tests.
From what I understand - You basically want to execute your test cases in a sequence. One after the other.
But since this is javascript, the frisby test cases are asynchronous. Hence, to make them synchronous, the documentation suggested you to nest the test cases. Now that is probably OK for a couple of test cases. But nesting would go chaotic if there are hundreds of test cases.
Hence, we use sequenty - another nodejs module, which uses call back to execute functions(and test cases wrapped in these functions) in sequence. In the afterJSON block, after all the assertions, you have to do a call back - cb()[which is passed to your function by sequenty].
https://github.com/AndyShin/sequenty
var sequenty = require('sequenty');
function f1(cb){
frisby.create('Create a `User`')
.post('http://localhost/users', { ... }, {json: true})
.expectStatus(200)
.afterJSON(function (json) {
//Do some assertions
cb(); //call back at the end to signify you are OK to execute next test case
})
.toss();
}
function f2(cb){
// User Login Test
frisby.create('Login `User`')
.post('http://localhost/users/login', { ... }, {json: true})
.expectStatus(200)
.afterJSON(function (json) {
// Some assertions
cb();
})
.toss();
}
sequenty.run(f1,f2);

Resources