effect icon indicating copy to clipboard operation
effect copied to clipboard

@effect/vitest when using `layer()` breaks Jetbrains IDE test navigation

Open dmeehan1968 opened this issue 7 months ago • 2 comments

What version of Effect is running?

3.14.18

What steps can reproduce the bug?

@effect/vitest version: 0.21.3

When you want to provide a layer for a set of tests, using

layer(MyLayer)((it) => describe...)

prevents Jetbrains IDE's from navigating through the tests. The IDE provides a specific view for executed tests, which consists of the suites and test names in a hierarchical view, with a preview of the test output on the right.

When the test is passing, clicking the test name should navigate the editor to the test name (it(...) line).

When the test is failing, the line within the test should be highlighted with a yellow underline, and clicking the test name should navigate to the line (typically an expect() statement) that is highlighted.

When the layer() function is used to create a custom it() that will inject the layer, neither of these navigations is possible. Typically it only navigates the editor to the correct file, but not the correct part of the tests.

This only happens when the layer() function is used.

As I was on an existing project with Jest as the test runner, I had created my own version of layer() and it.effect() which is where I encountered this problem. I created a separate project in order to try with @effect/vitest and found the same issue. As a result I tried different ways to do the layer injection, and I think the problem may be that Jetbrains use a pattern matching to locate the selected test, which expects the describe('<suitename>') to be contiguous.

In my Jest implementation, I settled on a custom describe that allows the second argument to be the layer name, and through overloading of the signature allowed for it to be omitted, so these are all acceptable:

describe("suite 1", (it) => {
  it('test1', () => /* effect */)
})

describe("suite 2 with layer", MyLayer, (it) => {
  it('test1', () => /* effect */)
})

NB: I tried different positions for the layer argument, and the Jetbrain's navigation fails when it is the first argument. Second argument seems quite logical, and putting it at the end seems least logical as its an input condition.

I was also able to elide the Layer types (ROut) so that the layer must provide all of the Requirements specified by the effect (R), and to ensure that the provided layer doesn't have any input Requirements (RIn).

What is the expected behavior?

It should be possible to navigate around the tests.

What do you see instead?

Broken test navigation

Additional information

It seems that it.effect etc extensions don't interfere with navigation, but it would be better if the vitest extensions were less invasive on the format of the file.

It might be better if extensions were added to the describe block that allow for customisation, for example:

describe('MySuite', ({ it, useLayer }) => {
  useLayer(MyLayer)
  
  it('should work as expected', () => Effect.succeed(undefined))
})

dmeehan1968 avatar May 07 '25 17:05 dmeehan1968

Two things:

  1. this is not a bug of Effect, if anything it could be considered a bug of JetBrains, to be submitted to JetBrains.
  2. there are reasons why APIs are like they are, the proposed changes like:
describe('MySuite', ({ it, useLayer }) => {
  useLayer(MyLayer)
  
  it('should work as expected', () => Effect.succeed(undefined))
})

can't work because TS won't be able to infer that the it has the context of MyLayer available.

Please open a concrete PR with tests for the proposed changes to be properly evaluated.

mikearnaldi avatar May 08 '25 08:05 mikearnaldi

I agree, but its a compatibility issue because the way in which @effect/vitest extends vitest.

As mentioned, I'm using my own extensions for use with Jest, but found the issue is the same (probably because jest/vitest have very similar styles and the Jetbrains test runners work with that. With the @effect/vitest implementation, you're asking IDE vendors to make Effect aware runners. I'm not sure that's a winning strategy.

Moving the layer into the arguments for describe (as long as its not the first argument) works and you still get type inference, and at least to my eyes, its easier to read.

Image

You are very right about the second suggestion, I hadn't considered the type inference. Although I think the following would preserve that and maintain even closer compatibility with jest/vitest syntax:

describe('MySuite', () => {
  const it = useLayer(MyLayer)  
  // or perhaps const it = effectualIt(MyLayer) where the layer is optional

  it('should work as expected', () => Effect.succeed(undefined))
})

Would you entertain a PR for a @effect/jest package? It's may be able to switch my project from jest -> vitest but otherwise I'd prefer to stick with Jest and I think it might be useful for the Effect community to have broader testing support, but I think a common approach would be useful. I appreciate I may be missing some nuance to the design choices for vitest.

dmeehan1968 avatar May 08 '25 10:05 dmeehan1968