Reputation: 4574
I am implementing a custom Cypress command in TypeScript:
// support/commands.ts
const login = () => {
console.log('Logging in...');
};
Cypress.Commands.add('login', login);
declare namespace Cypress {
interface Chainable {
login: typeof login;
}
}
I try to call it using:
describe('Login Scenario', () => {
it('should allow a user to login', () => {
cy.visit('/');
cy.login();
});
});
Yet, it seems the command is not set up:
TypeError: cy.login is not a function
If I write the command in pure JavaScript (removing the namespace declaration and updating the call to (cy as any).login();
, it works.
What am I missing?
Upvotes: 21
Views: 20990
Reputation: 51
I was having the same issue in my project which is using NX. I installed Cypress for testing my Angular app, and it generated the following files:
frontend/
├─ cypress/
│ ├─ e2e/
│ ├─ fixtures/
│ ├─ support/
│ │ ├─ app.po.ts
│ │ ├─ commands.js
│ │ ├─ commands.ts
│ │ ├─ component-index.html
│ │ ├─ component.js
│ │ ├─ e2e.ts
Inside component.js there was an import './commands'
line which was importing the commands.js
file by default instead of commands.ts
which is where I declared my custom command. I removed commands.js and it worked. I hope this helps somebody.
Upvotes: 0
Reputation: 919
Custom commands might not get imported, In cypress version 10 using angular/typescript schematic, there is a e2e.ts rather than index in support folder that imports the commands, if not then it needs the index.ts file with below import command, also it needs to be added to cypress config as supportFile:
// cypress/support/e2e.ts
// When a command from ./commands is ready to use, import with `import './commands'` syntax
import './commands';
It might already be commented in the file as well
Upvotes: 4
Reputation: 858
Here is what I use and I do not have to add
/// <reference types="cypress" />
at the top of every file.
I have my custom typings under <projectroot>/cypress/support/index.d.ts
/// <reference types="cypress" />
declare namespace Cypress {
interface Chainable<Subject> {
getByDataTest(tag: string): Chainable<any>
}
}
And my <projectroot>/cypress/tsconfig.json
looks like
{
"compilerOptions": {
"strict": true,
"baseUrl": "../node_modules",
"target": "es5",
"lib": ["es5", "dom"],
"typeRoots": ["./support"]
},
"include": ["**/*.ts"]
}
And TypeScript is finally happy
describe('when I want to select by data test tag', () => {
it('should select by data test tag', () => {
cy.getByDataTest('yolo').should('exist')
});
});
Upvotes: 7
Reputation: 9406
I fixed it by adding index.d.ts
file in my commands folder. In this file I added something like this:
import { MyCustomType } from '../Types';
declare global {
namespace Cypress {
interface Chainable<Subject = any> {
login(): Chainable<MyCustomType>;
}
}
}
If you don't import or export anything, just omit global namespace declaration:
declare namespace Cypress {
interface Chainable<Subject = any> {
login(): Chainable<MyCustomType>;
}
}
Keep in mind that it won't work with Typesciprt < 2.3, because default generics type has to be supported.
Upvotes: 17
Reputation: 4879
I had the same issue and all solutions I found did not work for me. Having done everything from the official Cypress documentation and other solutions here I still got the error cy.login is not a function
.
The problem was that I renamed every .js
file to .ts
and cypress/support/index.ts
was not loaded any more, because per default Cypress only loads the JavaScript one. To fix it, you need to change it to .ts
in cypress.json
like that (the same with plugins file):
{
"supportFile": "cypress/support/index.ts",
"pluginsFile": "cypress/plugins/index.ts"
}
You can also add the part with declare namespace Cypress {...
to commands.ts
instead of creating an index.d.ts
file, to have the declaration and implementation in the same file
Upvotes: 9