I am creating a cli app using oclif. The user executes a command, and the cli asks him if wants to continue (yes/no answer).
I trying to test the command that uses the cli-ux prompt. I want to simulate the user interaction to enter the 'yes' word.
How can I do that?
I tried this:
describe('mycommand', () => {
test
.stdout()
.command(['mycommand', 'action'])
.stdin('y')
.it('it shoud do someting', ctx => {});
});
Related with Oclif prompt testing I could find a solution.
Be careful how you ask the user because you can use cli.prompt or cli.confirm. In my case, I use cli.confirm so a possible test could be:
describe('it should clean items in done list', () => {
test
.stub(cli, 'confirm', () => async () => 'Y')
.stdout()
.command(['clean', 'done'])
.it('it shoud clean items in done list', ctx => {
// test
});
});
Related
I am working on setting up an automation test suite for an application using selenium and jest and it turns out that the console never reaches the inner body of the minimalist test case written below.
describe('When a user Opens Launchpad', () => {
test('It should be able to Navigate to Tasks Application without errors', async () => {
driver.get('http://localhost:4004/fiori.html').then(function () {
const temp = driver.findElement(By.xpath("li[#id='__tile11']"));
temp.then(function (element){
element.getAttribute("innerHTML");
expect(element.getText()).toBe("VT Dashboard");
})
});
}, 200000);
});
I looked online and tried multiple fixes like putting the driver.get() method above all these functions, making the test cases synchronous and using getText() instead of getAttribute() but none of them worked.
I either get an element not found error (The element actually exists when I check it on the chromium browser) or the test case executes successfully without reaching the expect statement in debug mode.
Bottomline is that driver.findElement() returns a promise instead of an element and would be great if I could get an element instead of promise.
Any help or correction here would be greatly appreciated.
If the function is async you should return the promise chain from that function or just use await keyword. So try following:
test('It should be able to Navigate to Tasks Application without errors', async () => {
await driver.get('http://localhost:4004/fiori.html');
const temp = await driver.findElement(By.xpath("li[#id='__tile11']"));
element.getAttribute("innerHTML");
expect(element.getText()).toBe("VT Dashboard");
}, 200000);
or
test('It should be able to Navigate to Tasks Application without errors', async () => {
return driver.get('http://localhost:4004/fiori.html').then(function () {
const temp = driver.findElement(By.xpath("li[#id='__tile11']"));
temp.then(function (element){
element.getAttribute("innerHTML");
expect(element.getText()).toBe("VT Dashboard");
})
});
}, 200000);
I am trying to intercept some requests in cypress following the click of a button, and I am trying to check if requests were or not made to a list of urls. I used a variable ( isRequestCalled ) as you can see, to update it if the requests were made, but the updates don't seem to be reach the test. When a request is intercepted, the value of isRequestCalled is updated in that scope but it doesn't seem to be visible in the scope of the test. Has anyone encoutered this type of issue, I appreciate all suggestions. Thanks!
describe('E2E test', () => {
let isRequestCalled
beforeEach(() => {
isRequestCalled=false
cy.origin(Cypress.env('ORIGIN_URL'), () => {
localStorage.clear();
})
cy.wait(1000);
cy.visit(Cypress.env('BASE_URL'));
cy.intercept('*', (req) => {
isRequestCalled=Cypress.env("REQUEST_URLS").some((url)=>{
return req.url.includes(url)
}) || isRequestCalled
// cy.wrap(isRequestCalled).as('isRequestCalled')
if (isRequestCalled) {
req.alias = 'apiRequests'
}
}
)
})
it('Consent test: Deny', () => {
cy.get(`[data-testid='${Cypress.env('BANNER_TEST_ID')}']`).should('be.visible').log("Banner visible");
cy.get(`[data-testid='${Cypress.env('BUTTON1_TESTID')}']`).should('be.visible').log("Button 1 visible");
cy.get(`[data-testid='${Cypress.env('BUTTON2_TESTID')}']`).should('be.visible').log("Button 2 visible");
cy.get(`[data-testid='${Cypress.env('BUTTON1_TESTID')}']`).click();
cy.reload();
// cy.get('#isRequestCalled').then(isRequestCalled => {
// console.log(isRequestCalled)
// expect(isRequestCalled).to.eq(false)
// })
cy.wrap(isRequestCalled).should('eq',false)
});
it('Consent test: Allow', () => {
cy.get(`[data-testid='${Cypress.env('BANNER_TEST_ID')}']`).should('be.visible').log("Banner visible");
cy.get(`[data-testid='${Cypress.env('BUTTON1_TESTID')}']`).should('be.visible').log("Button 1 visible");
cy.get(`[data-testid='${Cypress.env('BUTTON2_TESTID')}']`).should('be.visible').log("Button 2 visible");
cy.get(`[data-testid='${Cypress.env('BUTTON2_TESTID')}']`).click();
cy.wait('#apiRequests')
});
});
When using cy.intercept() you actually have no need for any variable counters if you want to ensure that the intercepted call has been made. I would recommend aliasing all of the requests you want to intercept and simply waiting (cy.wait()) for them after you perform the action that should trigger the call
describe('E2E test', () => {
beforeEach(() => {
cy.intercept('firstRequestUrl').as('firstCall');
cy.intercept('secondRequestUrl').as('secondCall')
cy.intercept('thirdRequestUrl').as('thirdCall')
cy.origin(Cypress.env('ORIGIN_URL'), () => {
localStorage.clear();
})
cy.wait(1000); // look into removing that as it looks kinda redundant
cy.visit(Cypress.env('BASE_URL'));
})
it('Does smth', () => {
cy.wait('#firstCall')
cy.get('#secondCallTrigger').click()
cy.wait('#secondCall')
})
it('Does smth else', () => {
cy.get('#thirdCallTrigger').click()
cy.wait('#thirdCall')
})
})
P.S.Keep in mind that js/ts variables in Cypress are very tricky to use between contexts, when you declare something in the describe, hooks, or a different it, you can't easily reach it from another it block.
People use env variables or hacks like this for that, although as you can see there is a native functionality for it.
Be sure to check the rest of intercept documentation and aliases documentation for more information.
It could be that the cy.reload() is resetting isRequestCalled.
If any commands such as visit or reload causes the browser to reset, you will notice the browser clearing and it's a sure sign that variables set up globally such as isRequestCalled are also cleared.
I can't really tell the intention of some of the commands, but perhaps you could test the value before the reload.
cy.wrap(isRequestCalled).should('eq',false)
cy.reload()
Also, what is the trigger for requests intercepted by Cypress.env("REQUEST_URLS")?
If it's cy.visit(Cypress.env('BASE_URL')) then the intercept needs to come before the visit.
If it's cy.get(```[data-testid='${Cypress.env("BUTTON1_TESTID")}']```).click() then the ordering is ok.
I've been trying to get around writing functional tests for my services using jest.
The action in the tests gets resolved but the test never passed because it keeps complaining that the server is already in use.
startServer()
describe("api tests", () => {
it("createUser: should create user successfully", async () => {
const user = await createUserService(userCredentials)
expect(user.email).toBe("test2#test.com")
}, 12000)
})
If i check my database, the user actually gets created in the process but for some reasons the tests does't pass and it return an error saying port address in use.
I have also tried to call startServer() in jest's beforeAll(() => {}). It still returns the same behaviour.
I want to stop a whole describe of JEST without throwing an error or stoping the other describes.
Im writing e2e test for my app with JEST and PUPPETEER, I write the test in a way every DESCRIBE its a flow of the path and every IT its a step, inside a IT I want to stop the flow if the pages dont match some conditions.
describe('Book a Room', ()=> {
it ('enter on main page' async() => await mainPage.navigateToMainPage())
it('go to book room page', async() => await bookRoomPage.navigateToBookRoomPage())
// The function its inside the "bookRoomPage"
it('check if the user can book room', () => {
if (!page.userCanOpenARoom()) {
// DONT EXECUTE THE NEXT IT BUT CONTINUE WITH THE OTHER DESCRIBE
}
})
it('go to book preview...', async() => bookRoomPreviewPage.navigateToBookRoomPreviewPage());
// REMAINING FLOW
})
I already try with process.exit(0) but exit the whole process
You can try out what this blog says here its for sharing specs in your test suites which is pretty handy. But for your case specifically you could extract your page cases in separate suites and then dynamically include the test case on runtime if a condition is met.
Something like:
Include Spec function shared_specs/index.js
const includeSpec = (sharedExampleName, args) => {
require(`./${sharedExampleName}`)(args);
};
exports.includeSpec = includeSpec;
Test A shared_specs/test_a.js
describe('some_page', () => {
it...
})
Test B shared_specs/test_b.js
describe('some_other_page', () => {
it...
})
and then in your test case
// Something like this would be your path I guess
import {includeSpec} from '../shared_specs/includeSpec.js'
describe('Book a Room', async ()=> {
if (page.userCanOpenARoom()) {
includeSpec('test_a', page);
} else {
includeSpec('test_b', page); // Or dont do anything
}
});
Just make sure that you check the paths since
require(`./${sharedExampleName}`)(args);
will load it dynamically at runtime, and use includeSpec in your describe blocks not it blocks. You should be able to split up your test suites pretty nicely with this.
I'm stuck at the very beginning, simply requiring the CLI and capturing its output. I've tried two methods but both don't work.
This is my cli.js:
#!/usr/bin/env node
console.log('Testing...');
process.exit(0);
And this my cli.test.js:
test('Attempt 1', () => {
let stdout = require("test-console").stdout;
let output = stdout.inspectSync(function() {
require('./cli.js');
});
expect(output).toBe('Testing...');
});
test('Attempt 2', () => {
console.log = jest.fn();
require('./cli.js');
expect(console.log.calls).toBe(['Testing...']);
});
Doesn't really matter which test is actually being run, the output is always:
$ jest
RUNS bin/cli.test.js
Done in 3.10s.
Node.js CLI applications are no different to other applications except their reliance on environment. They are expected to extensively use process members, e.g.:
process.stdin
process.stdout
process.argv
process.exit
If any of these things are used, they should be mocked and tested accordingly.
Since console.log is called directly for output, there's no problem to spy on it directly, although helper packages like test-console can be used too.
In this case process.exit(0) is called in imported file, so spec file early exits, and next Done output is from parent process. It should be stubbed. Throwing the error is necessary so that code execution is stopped - to mimic the normal behavior:
test('Attempt 2', () => {
const spy = jest.spyOn(console, 'log');
jest.spyOn(process, 'exit').mockImplementationOnce(() => {
throw new Error('process.exit() was called.')
});
expect(() => {
require('./cli.js');
}).toThrow('process.exit() was called.');
expect(spy.mock.calls).toEqual([['Testing...']]);
expect(process.exit).toHaveBeenCalledWith(0);
});