Best way to exeute various tests in protractor for various login id's - protractor

I have to test application using protractor for various different types of users. userid determines the type of user( Admin or partner or user). For all users i need to test the application for all major functionality. Here is what i want to do
Login User 1 > execute test1, test 2, test 3 .... Logout
Login user 2 > execute test1, test 2, test 3 .... Logout
Login User 3 > execute test1, test 2, test 3 .... Logout
Login User 4 > execute test1, test 2, test 3 .... Logout
I want to create a test framework to cover the scenario. I would appreciate inputs on best way to achieve this.

You could do this in a couple of ways. There are pro's and con's to each.
The first way is to just loop over the list of users.
describe('your suite', () => {
for (let user of users) {
describe('test for user: ' + user.username, () => {
beforeAll(() => { login(user) });
it('should do a test', () => {
// test code with user
});
it('should do another test', () => {
// test code with user
});
afterAll(() => { logout(user) });
});
}
});
Another way is to define your callback functions then compose your tests for each user.
testCallback1 = function() {
// test code with user
}
testCallback2 = function() {
// test code with user
}
describe('test for user'), () => {
let user = users[0];
beforeAll(() => { login(user) });
it('should do a test', testCallback1);
it('should do another test', testCallback2);
afterAll(() => { logout(user) });
});
describe('test for user'), () => {
let user = users[1];
beforeAll(() => { login(user) });
it('should do a test', testCallback1);
it('should do another test', testCallback2);
afterAll(() => { logout(user) });
});
There is probably a better way to do this. These are just a couple of suggestions.

I have a similar scenario and here is how I've tackled it. I added an array of users in the protractor config and then I can loop through those users to run my login tests. It's essentially the same setup as the first suggestion by cnishina in his answer. The difference for me is that my test code lives in one file and I have the code that gets the users and loops through them in another file. Probably overkill for a simple scenario but we have several different types of users (admin, non-admin, users with services, users without services, etc.) to run the login tests against so this helps to simplify that situation. I'm using TypeScript but the same thing can be done with JS if that's what you are using.
//in the protractor config
params: {
loginUsers: [
"login1#domain.test",
"login2#domain.test",
"login3#domain.test",
//and so on ...
]
}
Then I have a file where my login tests live called shared-login.tests.ts
export default class SharedLoginTests {
public static sharedSetup(url: string): void {
beforeEach(() => {
//shared setup code ...
}
afterEach(() => {
//shared tear down code ...
}
}
public static executeSharedLoginTests(username: string): void {
it(`should allow ${username} to login`, () => {
//login test code ...
}
}
}
And then I have login.tests.ts like this:
import { browser } from "protractor";
import SharedLoginTests from "./shared-login.tests";
describe(testtitle, () => {
const users: string[] = browser.params.loginUsers;
SharedLoginTests.sharedSetup(browser.params.baseUrl);
for (const user of users) {
SharedLoginTests.executeSharedLoginTests(user);
}
});

Related

I am developing VS Code extension and I need to capture the call stack records and log the result

I am writing a simple VS Code extension that suppose to just log the call stack in the console at specific point while debugging a code.
I was able to write a code to retrieve the current session of debugging, the break points and things like this, but I failed to find any property or method to allow me retrieve the call stack records.
This is the code I wrote:
export function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "sampleextension1" is now active!');
let disposable = vscode.commands.registerCommand('sampleextension1.hello', () => {
vscode.window.showInformationMessage('Hello World from sampleextension1!');
vscode.commands.executeCommand('editor.action.addCommentLine');
vscode.debug.onDidStartDebugSession(x => {
});
vscode.debug.onDidChangeActiveDebugSession(c => {
var b = vscode.debug.breakpoints[0];
});
});
context.subscriptions.push(disposable);
}
As you see in the code, there is an event handler for onDidChangeActiveDebugSession which enables me to capture the session of the debugging but no chance to find how to capture the stack trace.
I went through the documentation but it's not helpful though.
I was able to achieve what I want by sending a CutomRequest to the debugging session to retrieve the stack frames.
More information could be found in the DAP page here
The code is as shown below:
x.customRequest('stackTrace', { threadId: 1 }).then(reply => {
const frameId = reply.stackFrames[0].id;
}, error => {
vscode.window.showInformationMessage(`error: ${error.message}`);
});
or more efficient is to register tracker as shown below:
vscode.debug.registerDebugAdapterTrackerFactory('*', {
createDebugAdapterTracker(session: vscode.DebugSession) {
return {
onWillReceiveMessage: m => console.log(`> ${JSON.stringify(m, undefined, 2)}`),
onDidSendMessage: m => console.log(`< ${JSON.stringify(m, undefined, 2)}`)
};
}
});
The full example is shown here:
export function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "sampleextension1" is now active!');
let disposable = vscode.commands.registerCommand('sampleextension1.hello', () => {
vscode.window.showInformationMessage('Hello World from sampleextension1!');
vscode.commands.executeCommand('editor.action.addCommentLine');
vscode.debug.onDidStartDebugSession(x => {
// x.customRequest("evaluate", {
// "expression": "Math.sqrt(10)"
// }).then(reply => {
// vscode.window.showInformationMessage(`result: ${reply.result}`);
// }, error => {
// vscode.window.showInformationMessage(`error: ${error.message}`);
// });
x.customRequest('stackTrace', { threadId: 1 }).then(reply => {
const frameId = reply.stackFrames[0].id;
}, error => {
vscode.window.showInformationMessage(`error: ${error.message}`);
});
});
vscode.debug.onDidChangeActiveDebugSession(c => {
var b = vscode.debug.breakpoints[0];
});
vscode.debug.registerDebugAdapterTrackerFactory('*', {
createDebugAdapterTracker(session: vscode.DebugSession) {
return {
onWillReceiveMessage: m => console.log(`> ${JSON.stringify(m, undefined, 2)}`),
onDidSendMessage: m => console.log(`< ${JSON.stringify(m, undefined, 2)}`)
};
}
});
});
Steps to run:
F5 to run the Extension Dev Environment.
Ctl+Shift+P then write your cmd, in my case it was Hello
Then F5 to start the debugging in the Dev Environment then you will be able to see the result.
Hope it helps

Mocking authentication when testing MSAL React Apps

Our app is wrapped in the MSAL Authentication Template from #azure/msal-react in a standard way - key code segments are summarized below.
We would like to test app's individual components using react testing library (or something similar). Of course, when a React component such as SampleComponentUnderTest is to be properly rendered by a test as is shown in the simple test below, it must be wrapped in an MSAL component as well.
Is there a proper way to mock the MSAL authentication process for such purposes? Anyway to wrap a component under test in MSAL and directly provide test user's credentials to this component under test? Any references to useful documentation, blog posts, video, etc. to point us in the right direction would be greatly appreciated.
A Simple test
test('first test', () => {
const { getByText } = render(<SampleComponentUnderTest />);
const someText = getByText('A line of text');
expect(someText).toBeInTheDocument();
});
Config
export const msalConfig: Configuration = {
auth: {
clientId: `${process.env.REACT_APP_CLIENT_ID}`,
authority: `https://login.microsoftonline.com/${process.env.REACT_APP_TENANT_ID}`,
redirectUri:
process.env.NODE_ENV === 'development'
? 'http://localhost:3000/'
: process.env.REACT_APP_DEPLOY_URL,
},
cache: {
cacheLocation: 'sessionStorage',
storeAuthStateInCookie: false,
},
system: {
loggerOptions: {
loggerCallback: (level, message, containsPii) => {
if (containsPii) {
return;
}
switch (level) {
case LogLevel.Error:
console.error(message);
return;
case LogLevel.Info:
console.info(message);
return;
case LogLevel.Verbose:
console.debug(message);
return;
case LogLevel.Warning:
console.warn(message);
return;
default:
console.error(message);
}
},
},
},
};
Main app component
const msalInstance = new PublicClientApplication(msalConfig);
<MsalProvider instance={msalInstance}>
{!isAuthenticated && <UnauthenticatedHomePage />}
{isAuthenticated && <Protected />}
</MsalProvider>
Unauthenticated component
const signInClickHandler = (instance: IPublicClientApplication) => {
instance.loginRedirect(loginRequest).catch((e) => {
console.log(e);
});
};
<UnauthenticatedTemplate>
<Button onClick={() => signInClickHandler(instance)}>Sign in</Button>
</UnauthenticatedTemplate>
Protected component
<MsalAuthenticationTemplate
interactionType={InteractionType.Redirect}
errorComponent={ErrorComponent}
loadingComponent={LoadingComponent}
>
<SampleComponentUnderTest />
</MsalAuthenticationTemplate>
I had the same issue as you regarding component's test under msal-react.
It took me a couple of days to figure out how to implement a correct auth mock.
That's why I've created a package you will find here, that encapsulates all the boilerplate code : https://github.com/Mimetis/msal-react-tester
Basically, you can do multiple scenaris (user is already logged, user is not logged, user must log in etc ...) in a couple of lines, without having to configure anything and of course without having to reach Azure AD in any cases:
describe('Home page', () => {
let msalTester: MsalReactTester;
beforeEach(() => {
// new instance of msal tester for each test
msalTester = new MsalReactTester();
// spy all required msal things
msalTester.spyMsal();
});
afterEach(() => {
msalTester.resetSpyMsal();
});
test('Home page render correctly when user is logged in', async () => {
msalTester.isLogged();
render(
<MsalProvider instance={msalTester.client}>
<MemoryRouter>
<Layout>
<HomePage />
</Layout>
</MemoryRouter>
</MsalProvider>,
);
await msalTester.waitForRedirect();
let allLoggedInButtons = await screen.findAllByRole('button', { name: `${msalTester.activeAccount.name}` });
expect(allLoggedInButtons).toHaveLength(2);
});
test('Home page render correctly when user logs in using redirect', async () => {
msalTester.isNotLogged();
render(
<MsalProvider instance={msalTester.client}>
<MemoryRouter>
<Layout>
<HomePage />
</Layout>
</MemoryRouter>
</MsalProvider>,
);
await msalTester.waitForRedirect();
let signin = screen.getByRole('button', { name: 'Sign In - Redirect' });
userEvent.click(signin);
await msalTester.waitForLogin();
let allLoggedInButtons = await screen.findAllByRole('button', { name: `${msalTester.activeAccount.name}` });
expect(allLoggedInButtons).toHaveLength(2);
});
I am also curious about this, but from a slightly different perspective. I am trying to avoid littering the code base with components directly from msal in case we want to swap out identity providers at some point. The primary way to do this is to use a hook as an abstraction layer such as exposing isAuthenticated through that hook rather than the msal component library itself.
The useAuth hook would use the MSAL package directly. For the wrapper component however, I think we have to just create a separate component that either returns the MsalProvider OR a mocked auth provider of your choice. Since MsalProvider uses useContext beneath the hood I don't think you need to wrap it in another context provider.
Hope these ideas help while you are thinking through ways to do this. Know this isn't a direct answer to your question.

"NoSuchSessionError" encountered when running multiple scenarios using protractor+cucumber

I am getting this error:
NoSuchSessionError: This driver instance does not have a valid session ID (did you call WebDriver.quit()?) and may no longer be used.
In my test, I want to run multiple scenarios in one step file. I have read some questions/answers that I have to re-initialize my elements in step file.
When I try to run my code, I can see that the first scenario is performed and passed. But in my second scenario, I am not able to continue logging in to the page and stops after loginPage.get_url();
My feature file:
Scenario: As an admin I should be able to go to reports
Given Admin is in dashboard page
When Admin goes to Income reports
Then Admin should be able to click Download excel
Scenario: As an admin I should be able to log out
Given Admin goes back to dashboard page
When Admin clicks the logout button
Then Admin is now on login page
Here is my step file:
let chai = require('chai');
let chaiAsPromised= require('chai-as-promised');
chai.use(chaiAsPromised);
let expect = chai.expect;
const { Before, After } = require('cucumber');
var { setDefaultTimeout } = require('cucumber');
setDefaultTimeout(60 * 1200);
Given( 'Admin is in dashboard page', function () {
LoginPage.click_dashboard_button();
} );
When( 'Admin goes to Income reports', function () {
navPage.click_reports_dropdown();
browser.sleep(1000);
navPage.click_incomeReports();
} );
Then( 'Admin should be able to click Download excel', function () {
incomeReportsPage.click_downloadExcelButton();
browser.sleep(3000);
} );
Given( 'Admin go backs to dashboard page', function ( ) {
LoginPage.click_dashboard_button();
});
When( 'Admin clicks the logout button', function () {
LoginPage.click_logout_button();
});
Then( 'Admin is now on login page', function () {
browser.driver.getTitle().then( function ( value ) {
expect( value ).to.equal( "NAGM | Log in" );
});
});
Here is my Page Object file:
exports.init = function init() {
browser.waitForAngularEnabled( false );
}
exports.get_url = function get_url() {
browser.get( "http://nagm.test.pl/login" );
}
exports.input_username = function input_username() {
usernameTextbox.clear();
usernameTextbox.sendKeys("admin#nagm.ct");
}
exports.input_password = function input_password() {
passwordTextbox.clear();
passwordTextbox.sendKeys("password");
}
exports.click_login_button = function click_login_button() {
loginButton.click();
}
exports.click_logout_button = function click_logout_button() {
logoutButton.click();
}
exports.click_dashboard_button = function click_dashboard_button() {
dashboardMenu.click();
}
My hooks.js:
Before (function (){
browser.manage().window().maximize();
loginPage.init();
loginPage.get_url();
loginPage.input_username();
loginPage.input_password();
loginPage.click_login_button();
});

Unable to get Moxios stubRequest to work

I'm having issues getting stubRequest to work properly. Here's my code:
it('should stub my request', (done) => {
moxios.stubRequest('/authenticate', {
status: 200
})
//here a call to /authenticate is being made
SessionService.login('foo', 'bar')
moxios.wait(() => {
expect(something).toHaveHappened()
done()
})
})
This works fine:
it('should stub my request', (done) => {
SessionService.login('foo', 'bar')
moxios.wait(async () => {
let request = moxios.requests.mostRecent()
await request.respondWith({
status: 200
})
expect(something).toHaveHappened()
done()
})
})
The second method just get's the last call though, and I'd really like to be able to explicitely stub certain requests.
I'm running Jest with Vue.
I landed here with a similar goal and eventually solved it using a different approach that may be helpful to others:
moxios.requests has a method .get() (source code) that lets you grab a specific request from moxios.requests based on the url. This way, if you have multiple requests, your tests don't require the requests to occur in a specific order to work.
Here's what it looks like:
moxios.wait(() => {
// Grab a specific API request based on the URL
const request = moxios.requests.get('get', 'endpoint/to/stub');
// Stub the response with whatever you would like
request.respondWith(yourStubbedResponseHere)
.then(() => {
// Your assertions go here
done();
});
});
NOTE:
The name of the method .get() is a bit misleading. It can handle different types of HTTP requests. The type is passed as the first parameter like: moxios.requests.get(requestType, url)
it would be nice if you show us the service. Service call must be inside the moxios wait func and outside must be the axios call alone. I have pasted a simplified with stubRequest
describe('Fetch a product action', () => {
let onFulfilled;
let onRejected;
beforeEach(() => {
moxios.install();
store = mockStore({});
onFulfilled = sinon.spy();
onRejected = sinon.spy();
});
afterEach(() => {
moxios.uninstall();
});
it('can fetch the product successfully', done => {
const API_URL = `http://localhost:3000/products/`;
moxios.stubRequest(API_URL, {
status: 200,
response: mockDataSingleProduct
});
axios.get(API_URL, mockDataSingleProduct).then(onFulfilled);
const expectedActions = [
{
type: ACTION.FETCH_PRODUCT,
payload: mockDataSingleProduct
}
];
moxios.wait(function() {
const response = onFulfilled.getCall(0).args[0];
expect(onFulfilled.calledOnce).toBe(true);
expect(response.status).toBe(200);
expect(response.data).toEqual(mockDataSingleProduct);
return store.dispatch(fetchProduct(mockDataSingleProduct.id))
.then(() => {
var actions = store.getActions();
expect(actions.length).toBe(1);
expect(actions[0].type).toBe(ACTION.FETCH_PRODUCT);
expect(actions[0].payload).not.toBe(null || undefined);
expect(actions[0].payload).toEqual(mockDataSingleProduct);
expect(actions).toEqual(expectedActions);
done();
});
});
});
})

how to deal with mongodb race condition in integration test

I have a mongoose schema with a unique field and I am trying to write a backend (express) integration test which checks that POSTing the same entity twice results in HTTP 400. When testing manually behaviour is as excpected. Automatic testing however requires a wait:
it('should not accept two projects with the same name', function(done) {
var project = // ...
postProjectExpect201(project,
() => {
setTimeout( () => {
postProjectExpect400(project, done);
},100);
}
);
});
The two post... methods do as named and the code above works fine, but if the timeout is removed, BOTH requests receive HTTP 200 (though only one entity created in the database).
I'm new to those technologies and I'm not sure what's going on. Could this be a mongodb related concurrency issue and if so how should I deal with it?
The database call looks like this:
Project.create(req.body)
.then(respondWithResult(res, 201))
.catch(next);
I already tried connecting to mongodb with ?w=1 option btw.
Update:
To be more verbose: Project is a mongoose model and next is my express error handler which catches the duplicate error.
The test functions:
var postProjectExpect201=function(project, done, validateProject) {
request(app)
.post('/api/projects')
.send(project)
.expect(201)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
validateProject && validateProject(res.body);
done();
});
};
var postProjectExpect400=function(project, done) {
request(app)
.post('/api/projects')
.send(project)
.expect(400)
.end((err, res) => {
if (err) {
return done(err);
}
done();
});
};