I am new to e2e testing. I am writing test cases for angular login page its working in login but i am unable to navigate to homePage after checking login credential
I am getting this in console.
It will route to home page When username password is correct then go to home page
Failed: No element found using locator: By(css selector, app-homepage h1)
should Login work when login is successful — he should redirect to default home page page
Failed: No element found using locator: By(css selector, app-homepage h1)
this is my spec file
import { LoginPage } from './login.po';
import { HomePage } from '../HomePgE2E/home.po';
describe('should Login work', () => {
let page: LoginPage;
let homePage:HomePage;
const wrongCredentias = {
username: '',
password: ''
};
beforeEach(() => {
page = new LoginPage();
homePage= new HomePage();
});
it('when user trying to login with wrong credentials he should stay on “login” page', () => {
page.navigateTo();
page.fillCredentials(wrongCredentias);
expect(page.getPageTitleText()).toEqual('Sign in to your account');
expect(page.getErrorMessage()).toEqual('Email cannot be empty.');
});
it('when login is successful — he should redirect to default home page page ', () => {
page.navigateTo();
page.fillCredentials();
homePage.navigateTo();
expect(homePage.getPageTitleText()).toEqual('welcome');
});
});
And this welcome text coming from
import { browser, by, element } from 'protractor';
export class HomePage {
navigateTo() {
return browser.get('/home');
}
getPageTitleText() {
return element(by.css('app-homepage h1')).getText();
}
}
Below code should work
import { LoginPage } from './login.po';
import { HomePage } from '../HomePgE2E/home.po';
describe('should Login work', () => {
let page: LoginPage;
let homePage:HomePage;
const wrongCredentias = {
username: '',
password: ''
};
beforeEach(async async () => {
page = await new LoginPage();
homePage=await new HomePage();
});
it('when user trying to login with wrong credentials he should stay on “login” page', async () => {
await page.navigateTo();
await page.fillCredentials(wrongCredentias);
expect(page.getPageTitleText()).toEqual('Sign in to your account');
expect(page.getErrorMessage()).toEqual('Email cannot be empty.');
});
it('when login is successful — he should redirect to default home page page ', () => {
await page.navigateTo();
await page.fillCredentials();
await homePage.navigateTo();
expect(homePage.getPageTitleText()).toEqual('welcome');
});
});
Related
After logging in I call await router.push('/'); to redirect to the home page where I load users and I get this error GET http://localhost:8080/users 401 then when I refrehs the page in the exact same component I get the data just fine with a 200 status. I'm not sure what's going on
async login (username, password) {
const response = await axios.post('/auth/login', {
username: username,
password: password
});
this.user = response.data;
localStorage.setItem('user', JSON.stringify(this.user));
await router.push('/');
},
This is the function I call after logging in
This is the router.js
import { createRouter, createWebHistory } from 'vue-router';
import Login from '../views/Auth/Login.vue';
import { useAuthStore } from '../stores/auth.store.js';
import IndexUser from "../views/Users/IndexUser.vue";
import IndexHive from '../views/Hives/IndexHive.vue';
const routes = [
{ path: '/', name: 'Home', component: IndexUser },
{ path: '/login', name: 'Login', component: Login },
{ path: '/users', redirect: { name: 'Home' } },
{ path: '/users/create', name: 'CreateUser', component: CreateUser },
{ path: '/hives', name: 'IndexHive', component: IndexHive }
];
import CreateUser from '../views/Users/CreateUser.vue';
const router = createRouter({
history: createWebHistory(),
routes
});
router.beforeEach(to => {
const authStore = useAuthStore();
const publicPages = ['/login'];
const authRequired = !publicPages.includes(to.path);
if (authRequired && !authStore.user) {
return '/login';
}
})
export default router;
This is the component I redirect to after logging in
onMounted( async () => {
const response = await axios.get('/users');
users.value = response.data;
})
Devtools
Network tab
Axios Error
details of request/response
Response of login
Update 2
Having seen the code, I think the problem is here:
import axios from "axios";
axios.defaults.baseURL = import.meta.env.VITE_API_URL;
if (localStorage.getItem('user')) {
const user = JSON.parse(localStorage.getItem('user'));
axios.defaults.headers.common['Authorization'] = `Bearer ${user?.accessToken}`;
}
this will read the axios.defaults.headers when the helpers/axios.js file is loaded. This is why axios.get('/users'); only works on second load, or rather only when the authentication is already loaded into localStorage. A change to the user object or a local storage will not update since this code only runs once at the beginning, the change to axios.defaults.headers needs to be dynamic.
Update
if setTimeout didn't work that could be due to a different issue. Also, if your request works a second time, but it also works if the authentication is passed directly, it seems to me that it has something to do with the authentication being handled implicitly.
I think what's happening is that you are creating multiple instances of axios and relying on shared authentication
// create single axios instance
export const api = axios.create({
withCredentials: true,
baseURL: BASE_URL // optional
})
// then use
await api.post('/auth/login', {
username: username,
password: password
});
// and
await api.get('/users');
This might make the axios instance remember the authentication information between calls. It may still require handling race condition if you have an app that doesn't wait on the login request to finish.
I think this is just an issue with a race condition
POST:/login and GET:/users requests appear to be done in parallel.
onMounted( async () => {
// this should wait until the `login` has been handled
const response = await axios.get('/users');
users.value = response.data;
})
I don't see how you call login so can't offer the the exact solution, but if you can store the login request state as a reactive variable, you can do something like
watch: {
loginState:{
immediate: true
handler(value){
if (value === LOADED) {
const response = await axios.get('/users');
users.value = response.data;
}
}
}
})
here's what the changes to the authStore might look like
export const STATES = {
INIT:"INIT",
PROCESSING:"PROCESSING",
ERROR:"ERROR",
LOADED:"LOADED",
}
export const loginState = ref(STATES.INIT);
async login (username, password) {
loginState.value = STATES.PROCESSING
try{
const response = await axios.post('/auth/login', {
username: username,
password: password
});
loginState.value = STATES.LOADED
this.user = response.data;
localStorage.setItem('user', JSON.stringify(this.user));
await router.push('/');
}catch(e){
// handle error
loginState.value = STATES.ERROR
}
},
I have added some logic in my pages middleware (using Next 12) and would like to add tests now but am pretty lost on how to get that started. Can someone direct me to a tutorial or resource that shows a complete example of middleware being tested?
Specifically this is what my middleware is doing:
export function middleware(request: NextRequest) {
// Redirect a user if they don't have an auth token and are not the admin role
if (request.nextUrl.pathname.startsWith('/admin')) {
const authTokenCookie = request.cookies.token;
const parsedToken = authTokenCookie ? jose.decodeJwt(authTokenCookie) : null;
const role = typeof parsedToken === 'object' ? parsedToken?.role : null;
if (!authTokenCookie || !role || role !== USER_ROLES.admin) {
return NextResponse.redirect(new URL('/', request.url));
}
}
// Redirect the user if a query parameter is present
if (request.nextUrl.pathname === '/' && request.nextUrl.searchParams.has('directToStore')) {
NextResponse.redirect(new URL('/store', request.url));
}
return NextResponse.next();
}
This is how I ended up testing my middleware:
import { middleware } from '../pages/_middleware';
import { NextResponse, NextRequest } from 'next/server';
import * as jose from 'jose';
describe('Middleware', () => {
const redirectSpy = jest.spyOn(NextResponse, 'redirect');
afterEach(() => {
redirectSpy.mockReset();
});
it('should redirect to the homepage if visiting an admin page as a user without an auth token', async () => {
const req = new NextRequest(new Request('https://www.whatever.com/admin/check-it-out'), {});
req.headers.set(
'cookie',
'token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NTg3NjczMjYsImV4cCI6MTY5MDMwMzMyNiwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsInJvbGUiOiJ1c2VyIn0.G7rkptAKt1icBp92KcHYpGdcWOnn4gO8vWiCMtIHc0c;',
);
const { role } = jose.decodeJwt(req.cookies.token);
await middleware(req);
expect(role).toEqual('user');
expect(redirectSpy).toHaveBeenCalledTimes(1);
expect(redirectSpy).toHaveBeenCalledWith(new URL('/', req.url));
});
it('should redirect to the store if the directToStore query param is set', async () => {
const req = new NextRequest(new Request('https://www.whatever.com'), {});
req.nextUrl.searchParams.set('directToStore', 'true');
await middleware(req);
expect(redirectSpy).toHaveBeenCalledTimes(1);
expect(redirectSpy).toHaveBeenCalledWith(new URL('/store', req.url));
});
});
I need to decide whether to go with Playwright as an E2E Automation tool or not.
The following piece of code using backend API for sign in is working fine.
But when I use the saved Storagestate file inside the BrowserContext, then it fails to sign me in, hence taking me back to the Login page.
test.describe('User login', async () => {
var browser: any;
test.beforeEach(async () => {
browser = await chromium.launch({
channel: 'chrome',
headless: false
});
});
const requestContext = await request.newContext();
const response = await requestContext.post(`${testConfig.envConfig.apiURL}/login/authorize`, {
form: {
'email': 'user.tech+admin#xxxx.com',
'password': 'xxxxxx'
}
});
const responseJson = await response.json();
console.log(responseJson);
expect(response.ok()).toBeTruthy();
// Save signed-in state.
await requestContext.storageState({ path: './.storagestates/adminUserStorageState.json' });
await requestContext.dispose();
// Using the Storagesate in the BrowserContext
// page should sign in as a admin user.
test.use({ storageState: './.storagestates/adminUserStorageState.json' });
test('Version 1: Cookies stored via Auth. by backend API', async ({ page }) => {
let url = `/brand/f99f06fb1f121234aaf/gallery`;
await page.goto(url, {timeout: 60000});
});
test('Version 2: Cookies stored via Auth. by backend API', async () => {
// page should sign in as a buyer user.
const userContext = await browser.newContext({ storageState: './.storagestates/buyerUserStorageState.json' });
const page = await userContext.newPage();
await page.goto(`/`, {timeout: 60000});
});
});
I am testing Auth0 with angular for the first time and have run into an issue. I let my user login trough Auth0 and after they have logged in I redirect them to my dashboard page that is protected with Auth0 guard. I have managed to redirect the user but after they log in the URL shows that the user is on the dashboard route however they can't see the actual dashboard content. I tried to see what is going on and I think the problem is that the user is logged in but the auth guard boolean is never set to true which makes my program continuously refresh the URL. When I take the auth guard off the route the redirect works and shows the content. I have played around with the code hoping to figure it out but so far no luck.
I will share my code if I have to share more code please let me know.
app.module.ts
const appRoutes: Routes = [
{ path: 'app-dashboard', component: DashboardComponent,
canActivate: [AuthGuard]
},
{ path: 'login', component: LoginComponent },
{ path: 'app-shell-start', component: ShellStartComponent },
{
path: '',
redirectTo: '/app-shell-start',
pathMatch: 'full'
},
];
auth.guard.ts
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService, public router:Router) {}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean|UrlTree> | boolean {
console.log("start")
return this.auth.isAuthenticated$.pipe(
tap(loggedIn => {
console.log("loggedIn",loggedIn)
this.router.navigate(['']);
if (!loggedIn) {
this.auth.login(state.url);
}
})
);
}
}
auth.services.spec.ts
import { TestBed } from '#angular/core/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
auth.service.ts
#Injectable({
providedIn: 'root'
})
export class AuthService {
// Create an observable of Auth0 instance of client
auth0Client$ = (from(
createAuth0Client({
domain: "techradartest.eu.auth0.com",
client_id: "KxM7ICZKf08Mt5czJnxxD3a47HwH3jE1",
redirect_uri: "http://localhost:4200/app-dashboard" // `${window.location.origin}`
})
) as Observable<Auth0Client>).pipe(
shareReplay(1), // Every subscription receives the same shared value
catchError(err => throwError(err))
);
// Define observables for SDK methods that return promises by default
// For each Auth0 SDK method, first ensure the client instance is ready
// concatMap: Using the client instance, call SDK method; SDK returns a promise
// from: Convert that resulting promise into an observable
isAuthenticated$ = this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.isAuthenticated())),
tap(res => this.loggedIn = res)
);
handleRedirectCallback$ = this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.handleRedirectCallback()))
);
// Create subject and public observable of user profile data
private userProfileSubject$ = new BehaviorSubject<any>(null);
userProfile$ = this.userProfileSubject$.asObservable();
// Create a local property for login status
loggedIn: boolean = null;
constructor(private router: Router) {
// On initial load, check authentication state with authorization server
// Set up local auth streams if user is already authenticated
this.localAuthSetup();
// Handle redirect from Auth0 login
this.handleAuthCallback();
}
// When calling, options can be passed if desired
// https://auth0.github.io/auth0-spa-js/classes/auth0client.html#getuser
getUser$(options?): Observable<any> {
return this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.getUser(options))),
tap(user => this.userProfileSubject$.next(user))
);
}
private localAuthSetup() {
// This should only be called on app initialization
// Set up local authentication streams
const checkAuth$ = this.isAuthenticated$.pipe(
concatMap((loggedIn: boolean) => {
if (loggedIn) {
// If authenticated, get user and set in app
// NOTE: you could pass options here if needed
return this.getUser$();
}
// If not authenticated, return stream that emits 'false'
return of(loggedIn);
})
);
checkAuth$.subscribe();
}
login(redirectPath: string = '/') {
// A desired redirect path can be passed to login method
// (e.g., from a route guard)
// Ensure Auth0 client instance exists
this.auth0Client$.subscribe((client: Auth0Client) => {
// Call method to log in
client.loginWithRedirect({
redirect_uri: "http://localhost:4200/app-dashboard", //`${window.location.origin}`,
appState: { target: redirectPath }
});
});
}
private handleAuthCallback() {
// Call when app reloads after user logs in with Auth0
const params = window.location.search;
debugger;
if (params.includes('code=') && params.includes('state=')) {
console.log("loggedIn handleAuthCallback")
let targetRoute: string; // Path to redirect to after login processsed
const authComplete$ = this.handleRedirectCallback$.pipe(
// Have client, now call method to handle auth callback redirect
tap(cbRes => {
// Get and set target redirect route from callback results
targetRoute = cbRes.appState && cbRes.appState.target ? cbRes.appState.target : '/';
}),
concatMap(() => {
// Redirect callback complete; get user and login status
return combineLatest([
this.getUser$(),
this.isAuthenticated$
]);
})
);
// Subscribe to authentication completion observable
// Response will be an array of user and login status
authComplete$.subscribe(([user, loggedIn]) => {
// Redirect to target route after callback processing
console.log("targetRoute: ",targetRoute)
this.router.navigate([targetRoute]);
});
}
}
logout() {
// Ensure Auth0 client instance exists
this.auth0Client$.subscribe((client: Auth0Client) => {
// Call method to log out
client.logout({
client_id: "KxM7ICZKf08Mt5czJnxxD3a47HwH3jE1",
returnTo: `${window.location.origin}`
});
});
}
}
I am using Angular 9
I'm a newbie in NuxtJs. I'm trying to implement an external API Call with axios which I get token and store it on cookie. Everything works well in development. But when I try to run npm run generate it gives me errors that I don't know what to do.
When I delete nuxtSeverInit, npm run generate runs smoothly. And after some research, i think that nuxtServerInit that I'm using shouldn't be used. Can anyone please tell me how to make it work.
This is the first project in a new company, so I'm trying to prove myself. Please help me with it. Will you.
Click here for image that shows the error that appears after npm run generate
This is store/index.js file
import Vuex from 'vuex'
var cookieparser = require('cookieparser')
const createStore = () => {
return new Vuex.Store({
state: {
auth: null,
},
mutations: {
update (state, data) {
state.auth = data
}
},
actions: {
nuxtServerInit ({ commit }, { req }) {
let accessToken = null
if (req.headers.cookie) {
var parsed = cookieparser.parse(req.headers.cookie)
if(parsed){
accessToken = parsed.auth
}
}
commit('update', accessToken)
},
}
})
}
export default createStore
middleware/authenticated.js file
export default function ({ store, redirect }) {
// If the user is not authenticated
if (!store.state.auth) {
return redirect('/login')
}
}
middleware/notAuthenticated.js file
export default function ({ store, redirect }) {
// If the user is authenticated redirect to home page
if (store.state.auth) {
return redirect('/app/dashboard')
}
}
login.vue file
validateBeforeSubmit() {
this.$validator.validateAll().then((result) => {
if (result) {
this.button_title = 'One moment ...';
let submitted_user_data = {
'username': this.emailAddress,
'client_id': this.user_uuid,
'password': this.password,
}
MerchantServices.do_user_login(submitted_user_data)
.then(response => {
let access_token = response.data.access_token;
this.postLogin(access_token);
})
.catch(error => {
this.$refs.invalid_credentials.open();
this.button_title = 'Sign in'
});
return;
}
});
},
postLogin: function(access_token_val) {
if(access_token_val != ''){
setTimeout(() => {
const auth = {
accessToken: access_token_val
}
this.$store.commit('update', auth)
Cookie.set('auth', auth)
this.$refs.invalid_credentials.open();
this.button_title = 'Sign in'
this.$router.push('/app/dashboard')
}, 1000)
}else{
alert('hello')
}
},
and the last user login api call which also returns the token.
do_user_login(user){
var user_details = 'username='+user.username+'&client_id='+ user.client_id +'&grant_type=password&password='+user.password+''
return axios.post('myapiurl', user_details )
.then(response => {
return response;
});
},
Acording to Nuxt Docs req is not available on nuxt generate.
You should use nuxt build and than nuxt start after that.