Testcafe - Test multiple pages - javascript

I'm testing a website with a login page and then other pages only visible after login.
I created a login_page.js model as follows:
// my_login_page_model.js
import { Selector, t } from 'testcafe';
export default class LoginPage {
constructor () {
this.email = Selector('#email');
this.password = Selector('#password');
this.signin = Selector('#signinButton');
}
}
and then I created similar page models for the pages after login.
On my test file, I then instantiate a login page object, run the login page test
// my_test_spec.js
test('login in', async t => {
await t
.typeText(loginPage.email, config.name)
.typeText(loginPage.password, config.password)
.click(loginPage.signin);
await t.expect(getURL()).contains(pageUrl);
});
test('second test', async t => {
console.log('Creating a new experience');
await t
.click(// click a button on the following page);
});
The problem is the second test starts from the login page and of course, it fails because it can't find the ids of the page after login.
How can this be done?

Every test starts from the scratch and that means clean profile, that why you are not authorized, when second test starts. The simplest way to fix is to move you login code at the beginning of your second test. However, it's not good practice, because you probably need authorization before majority of your test. To solve this, you can choose one of this:
1) Http Auth for some cases
2) Roles
3) Use beforeEach Hook. Just put Login code to this hook and it's will execute before every test in fixture.

Related

React keycloak - How to use onAuthSuccess and onAuthLogout events?

I use keycloak on my React project as authentication and authorization tool and want to store some data of the user inside a React Context to access data like username etc. This context should update when I log in or log out.
My first approach was to use two events called onAuthSuccess and onAuthLogout but it seems that it will not be fired at all.
To test this approach I execute a console.log. Unfortunately nothing happens if I log in via keycloak.login() and logout via keycloak.logout().
import Keycloak from 'keycloak-js';
import configData from './config.json';
const keycloak = new Keycloak(configData);
keycloak.onAuthSuccess = () => {
console.log('log in!');
}
keycloak.onAuthLogout = () => {
console.log('log out');
}
export default keycloak
Any ideas what the problem could be?

React: change url without rerender; using window.history?

I have a "settings" page in my react app. The page has several tabs rendering different parts of settings.
It would be better UX if a user can share urls with other users.
What I want is (inside "settings" page):
user A clicks a tab
url changes with a #tabname appended
user A send that url to user B, and user B open that url
user B sees the same tab as user A
But with react router, the whole page re-renders if the url changed:
import { withRouter } from "react-router-dom"
const MyComp = (props) => {
...
const onTabChange = () => {
// append #tabname here
props.history.replace(...); // or `push`
...
}
...
export default withRouter(MyComp)
}
After a lot of searches, I found a solution to use window.history:
const onTabChange = () => {
window.history.pushState(null, null, "#tabname");
...
}
This does the trick, but little information and explanation, and I'd love to know the consequences of using this trick.
Is this a valid solution (for a react app)? Will this cause any problem?
(PS. I know how to parse a url)
More details:
To be more specific, there is a AuthChecker wrapper for all pages. When react router's location changes, it checks for the route's allowed auths and current user's auth.
I've tried /path/:id and everything but all change location, so auth checked and page rerendered.
And I've given up a solution in react router and just want to know: is it safe to change url with window.history in a react app using react router to manage routes?
this question is already answerd at this post.
so it says window has a property called history and there is a method on history which helps you update the history state without react-router-dom understanding it.
like this:
window.history.replaceState(null, 'New Page Title', '/new_url');

Is it recommended to have a mobx Store for each page?

I am building a single page application with Reactjs and MobX at the frontend (port 3000) and Nodejs and Express at the backend (API, port 4000). I am new to both, MobX and Reactjs and I am trying to set up a well-structured project.
My question is: Is it okay to have a Store for each view?
For example, I have a UserStore which stores the Session information and takes care of the login and logout of the user within the platform. However, after Logging in, I want to redirect the user to the dashboard page. This dashboard page must retrieve information regarding the user, but also it must contact the API and retrieve some data (i.e. Some Todos).
This is how I would do it:
This is the login function in which the redirection to Dashboard is made:
*UserStore.js*
[...]
import navigationStore from './NavigationStore';
[...]
login = async (user) => {
try {
const res = await axios.post('/session/login', {
username: user.username,
password: user.password
});
this.saveUser(res.data);
navigationStore.push('/dashboard');
} catch (error) {
[...]
}
}
And, then, I have created a DashboardStore.js which has the following code:
*DashboardStore.js*
[... imports and initializations ...]
class Store {
#observable todos = null
constructor() {
this.getDashboard();
}
#action('Load dashboard') getDashboard = async () => {
const res = await axios.get('/api/dashboard/', {});
this.todos = res.todos
}
}
const DashboardStore = new Store();
export default DashboardStore;
But this would mean that I'd end up doing another Store for the Todos page and another Store for whatever page I'd need.
In NodeJs you can make a controller for each class and there's nothing weird about it. However, I'm not sure that's how it works on MobX.
It depends on the complexity of your app. I wouldn't create a store for each view or concern, but you could create two, like the MobX docs recommend: https://mobx.js.org/best/store.html.
I'm working on a bigger project right now, and we started with a single store for everything. Obviously, it grew a lot as we kept adding functionality, so I think we might split it at some point to reduce complexity.

router.navigate not working in Angular or going into infinite loop

I'm using Angular 6. I have a service called UserService which is calling a function called getUser().
In the function I want to navigate to the login page when the user has an invalid token.
getUser(){
this.user = JSON.parse(sessionStorage.getItem('currentUser'));
if (!this.user) {
this.router.navigate(['login']);
this.toastr.error('Unknown Logged In User');
return false;
}
this.token = this.user.token;
return this.user;
}
I'm calling getUser in a lot of components. for example in the ActiveOrdersComponent
getCurrentUser() {
let loggedUser = this.userService.getUser();
if (loggedUser) {
this.info = {
currentUser: {
currentUserName: loggedUser.first_name + ' ' + loggedUser.last_name
}
}
}
return this.info.currentUser;
}
Im using LazyLoader.
My original path is '/auth/orders/active'
Destination Login path is '/login'
the function getUser() is not navigating and going into an infinite loop. and the browser is freezing with multiple console errors.
the function is returning false and continues to execute the code in the component (which will break because I'm returning false).
i have had this issue before and after some hours i could find the problem. there are 2 things happen when you face with this problem:
when you want to redirect user to another page and use navigate method, you should put the address like this :
this.router.navigate(['/login']
sometimes you write this code in its own component. for example, you write the code above in the login component. this causes that you face with infinite loop when you refreshing the page. just you should change the address in navigate method to put away this problem.
i hope that it helps you

ReactJS - Wait third party script response before render

I implemented J-Toker in my frontend app using React and flow (with a Rails API backend), in a similar way to this project react-flux-jwt-authentication-sample
The dashboard should only be accessed by logged in user. In order to do this, J-Toker calls my Rails API and return the loggedIn User (or not if no one is loggedIn...) that is accessible using Auth.user.
So I have my Dashboard component wrapped in an AuthenticatedComponent:
Dashobard.jsx
export default AuthenticatedComponent(Dashobard);
Here is my AuthenticatedComponent.jsx:
export default (ComposedComponent) => {
return class AuthenticatedComponent extends React.Component {
static willTransitionTo(transition) {
if (!LoginStore.isLoggedIn()) {
transition.redirect('/login', {}, {'nextPath' : transition.path});
}
}
...
}
Finally here is my LoginStore.jsx code:
import Auth from 'j-toker';
class LoginStore extends BaseStore {
...
isLoggedIn() {
return Auth.user.signedIn;
}
}
Everything works great except when I manually refresh the page. In this case, the Auth.user.signedIn return undefined even if the user is logged in and is redirected to the login page. I found that the problem comes from the fact that Auth.user is not yet loaded when LoginStore.isLoggedIn() is called because in this case Auth.user returns an empty object and if I wait a little (tested with a basic setTimeout) it returns the loggedIn user.
I am new to React.JS (using it for just a week now) and I'm not sure how to deal with this. I read A LOT of articles and cannot understand yet how React.JS is meant to be used, espacially with third party plugins (I also have a lot of problems using animation plugins but it's another story...).
Could someone help me on this?
It would be much appreciated :)
This happens because when you pass the configuration to J-Toker, the library will check for a stored token. In case it finds one it will validate the token with you API. This is why calling configure, it will return you a jquery Deferred object, which will be resolved after the validation has been done or rejected if there is no stored token. If the validation passes in you API the user object will be returned and set in Auth.user.
By following your example project you can fix this by rendering the component after the Deferred has been resolved.
var promise = Auth.configure({
apiUrl: 'your-api-host'
});
promise.always(function() {
router.run(function (Handler) {
React.render(<Handler />, document.getElementById('content'));
});
});

Categories