question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Improve DX for enigneers using TypeScript (or type checks)

See original GitHub issue

What are you trying to achieve?

I’m trying to configure our code base in a way that would reliably warn us about type issues. In particular I’d like to see correct type errors when I inject more than one page object into my Scenario.

This problem makes the CodeceptJS typings not usable in this specific case.

What do you get instead?

Instead, whenever I have more than one page object in the Scenario callback, I immediately get a lot of errors.

Details

Here is a dummy example (pseud-code):

Scenario('dum-dum', (I, pageA, pageC, pageB) => {
  pageA.doA();
  pageB.doB();
  pageC.doC();
});

Note that I have purposefully ordered arguments this way (pageC before pageB).

The following code will work fine in runtime but it will fail at “type check” time.

Note that the following is true even if you don’t use TypeScript but simply enable typechecking with the following jsconfig.json

{
  "compilerOptions": {
    "checkJs": true
  }
}

There reason this code fails comes from the way CodeceptJS injects page objects and how that affects ICodeceptCallback type (steps.d.ts).

CodeceptJS uses fn-args to inject page objects. It reads function’s arguments and it expects exact match between argument name and page object name declared in the config file.

The problem with this approach is that the generated ICodeceptCallback expects arguments to be passed in a particular order. In context of this example it could look similar to:

type ICodeceptCallback = (i?: CodeceptJS.I, current?:any, pageA: PageA, pageB: PageB, pageC: PageC, ...args: any) => void;

See that it expects the 3rd argument to be of type PageA. However, in runtime (and plain, unchecked javascript) it’s totally fine to actually put pageC there instead.

  • CodeceptJS version: 2.3.2

Proposed solution

All those problems would disappear if ICodeceptCallback expected just two arguments:

  • I
  • and custom

Where custom would be an object containing all user-defined page objects, fragments, etc.

This way the order of parameters would no longer matter and type checking would be happy 😃

If done this way this example scenario would look like so (I’m using destructuring):

Scenario('dum-dum', (I, { pageA, pageC, pageB }) => {
});

and generated ICodeceptCallback would become

type ICodeceptCallback = (i?: CodeceptJS.I, custom?: { pageA?: PageA, pageB?: PageB, pageC?: PageC }) => void;

Workaround

Possible workaround for people with the same problem is to use inject function at the top of a testing suite.

Instead of doing

Scenario('x', (I, page) => {})

do

const { page } = inject();
Scenario('x', (I) => {})

Although I don’t know whether that will be the same with regards to page object’s state.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:1
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
miraocommented, Dec 1, 2019

Great. Thanks for looking into it @chris-miaskowski. BTW I didn’t know about the refactoring feature in VSCode when moving/renaming file with module. It really works!

0reactions
miraocommented, Dec 4, 2019

I’ve just upgraded to CodeceptJS 2.3.6 and run npx codeceptjs def.

Now I don’t need to use const loginPage = require("./pages/Login"); to get correct IntelliSense (https://github.com/Codeception/CodeceptJS/issues/1972#issuecomment-560108412), because the page object definition is already imported in steps.d.ts, e.g.:

type loginPage = typeof import('./pages/Login.js');

So now when I’m clicking on a method of loginPage, it navigates to source of the method as expected.

Of course this issue #1972 is blocking autocomplete/intellisense if passed page objects don’t match order in steps.d.ts. Well, a workaround exists - inject objects globally as described in https://github.com/Codeception/CodeceptJS/issues/1972#issue-508517225

Read more comments on GitHub >

github_iconTop Results From Across the Web

Goodbye Typescript, hello native typing for Javascript
Typing. Love it or hate it, it has many advantages: better DX (through ... But you can also use the typescript compiler to...
Read more >
Code with pleasure. Typescript improves Developer Experience
With interactive documentation, user-friendly design, and balancing between dynamic and static typed code, Typescript improves Developer ...
Read more >
Methods for TypeScript runtime type checking - LogRocket Blog
Explore five methods of performing TypeScript type checks at runtime in this post and learn each of their advantages and disadvantages.
Read more >
Discriminated Unions and Exhaustiveness Checking in ...
In this article, you'll find an example of what we learned about discriminated unions and exhaustiveness checking in Typescript and why it's ...
Read more >
Gradually migrating to typescript across multiple js codebases ...
DX : we were already using prop types in the codebase for documenting a component ... Enable typescript on other codebases with better...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found