ember-browser-services icon indicating copy to clipboard operation
ember-browser-services copied to clipboard

Race condition for setupBrowserFakes vs. first lookup of service

Open simonihmig opened this issue 1 year ago • 5 comments

This addon has a flaw that I don't know how to best fix (without major changes). The problem is that opting into the mocked behavior is done by calling setupBrowserFakes() in tests, which will register the service (e.g. service:browser/window) with a mockable Proxy class here. However, owner.register() will only work when that service hasn't been used before. So that opens up the possibility to run into race conditions when service:browser/window has been looked up (e.g. by having an @service('browser/window') field be evaluated during app boot) before setupBrowserFakes() was called.

In that case, setupBrowserFakes(hooks, { window: { location: { href: 'https://example.com' } } }) would have no effect. The service would be the one directly proxying to the actual window global, not the Proxy from ember-window-mock. As such this.owner.lookup('service:browser/window').location.href would not be 'https://example.com' (but http://localhost:4200/tests or similar), and this.owner.lookup('service:browser/window').location.href = 'https://example.com'; would actually do a real redirect.

simonihmig avatar Sep 04 '24 15:09 simonihmig

:thinking: what causes the app to boot and how is it happening before setupBrowserFakes?

NullVoxPopuli avatar Sep 04 '24 15:09 NullVoxPopuli

One instance where this came up was rooted in an initializer running (apparently so before setupBrowserFakes), that would lookup a service, that has a connection (via @service) to the window service.

simonihmig avatar Sep 04 '24 16:09 simonihmig

ah, yeah -- initializers have proven to often be problematic -- because while it was browser-services hit this time, it more often affects ones own codebase / services -- or maybe a different addon's services (ember-intl's service is a common one also hit by this initializer behavior).

If possible, I think folks should move all their initializer logic to the application route and init there -- which also gets you async.

NullVoxPopuli avatar Sep 04 '24 16:09 NullVoxPopuli

I do wonder though why we need that timing-sensitive register behaviour. After all, ember-window-mock allows you to opt into mocked/proxy mode at any time. Can't the service just use that directly, like all the time?

simonihmig avatar Sep 04 '24 16:09 simonihmig

Can't the service just use that directly, like all the time?

yeah, I don't see a reason why it wouldn't be able to. Perhaps the original thinking was to "be as light as possible". Maybe it went too far.

NullVoxPopuli avatar Sep 04 '24 16:09 NullVoxPopuli