Support providing a fake Browser.Navigation.Key in tests - #243
Conversation
| ], | ||
| "elm-version": "0.19.0 <= v < 0.20.0", | ||
| "dependencies": { | ||
| "elm/browser": "1.0.2 <= v < 2.0.0", |
There was a problem hiding this comment.
I wonder whether this is as low as we can go, or whether 1.0.1 or 1.0.2 have something important we want to encourage adoption of.
(We did that with bytes 1.0.8 I think, where there was something substantial... but I can't remember the details)
There was a problem hiding this comment.
It looks like Browser.Navigation.Key used to be an object, not a function:
That commit is dated Jul 30, 2018 and 1.0.1 is the first tag after that: https://github.com/elm/browser/tags
So we could potentially lower this to 1.0.1 (after double-checking that stuff actually works), but I don't think there is any gain doing this in practice, since 1.0.2 was released in late 2019.
There was a problem hiding this comment.
I've lowered it to 1.0.1, that seems to work fine. In practice I believe we could lower it to 1.0.0 without any noticeable effect, except that Debug.log would show something slightly different just for 1.0.0. I think 1.0.1 is fine but happy to change it.
0d3059d to
5a40402
Compare
| } | ||
| } | ||
|
|
||
| function _Test_navigationKey() {} |
There was a problem hiding this comment.
Here’s an idea I just got:
| function _Test_navigationKey() {} | |
| function _Test_navigationKey() { | |
| throw new Error('This is a test Browser.Navigation.Key that must not be used for real!'); | |
| } |
And we could expose it like so:
-- module Test
{-| Exposed to users of the package! -}
navigationKey : Browser.Navigation.Key
navigationKey =
Elm.Kernel.Test.navigationKeyThen there is no need for new testWithKey and fuzzWithKey functions.
If someone were to try to use it for real in an app, it would compile. But at runtime, it would run a history method and then throw an error, breaking the app. (We should verify that it breaks the app enough, though.)
https://github.com/elm/browser/blob/1.0.2/src/Elm/Kernel/Browser.js#L190-L212
Alternatively, instead of throwing an error we could do something drastic like replacing the whole document.body with an error message.
Not sure if this is good or not, but it is tempting.
There was a problem hiding this comment.
Interesting idea!
Maybe we could make it fail (throw) unless something was set by the test runner (ex: globalThis.ELM_TESTING === true), though all test runners would have to change something which is painful unless there's already something we can check for.
There was a problem hiding this comment.
Oh, is it possible to execute the key function in tests? Or why would we need a guard?
There was a problem hiding this comment.
I am not sure what you mean. The guard is meant is to prevent people using this value in application (non-test) code.
Alternatively, we could have a design like this, resembling the original testWithKey of this PR, but with a value that allows you to build any "dangerous" value.
-- Names to be bikeshedded
testWithData : String -> (MasterKey -> Expectation) -> Test
test "title" <|
\masterKey ->
let navigationKey = Test.navigationKey masterKey
in
-- ...That way, if new values like Navigation.Key come up later in Elm's public API, we won't have to add new variants of test, we will only have to introduce a new function:
-- Test.elm
newResource : MasterKey -> ResourceBut it does mean we will need to solve the problem of all the test/fuzz variants that blocked this original PR.
People can easily recreate testWithKey with this API as well:
testWithKey title fn =
Test.testWithData title (Test.navigationKey >> fn)There was a problem hiding this comment.
The guard is meant is to prevent people using this value in application (non-test) code.
How? I didn’t understand what globalThis.ELM_TESTING === true is supposed to do. (I made a guess, but just confused you instead.)
There was a problem hiding this comment.
The idea was to do something like
function createNavigationKey() {
if (globalThis.ELM_TESTING !== true) {
throw new Error("This is only meant to be used in test code!");
}
return dummyNavigationKey;
}and have the test runners define this global constant somewhere so that this check would pass: globalThis.ELM_TESTING = true;.
That said, with the proposal in my previous comment, this is unnecessary.
There was a problem hiding this comment.
Here’s one more idea for how to provide the key without making new test and fuzz functions:
tests =
withKey <| \navKey ->
describe "my tests"
[
-- tests…
]There was a problem hiding this comment.
Yeah... that would work quite well!
withKey : (Navigation.Key -> Test) -> TestWhat do you think @Janiczek?
679a2ac to
1b7de65
Compare
|
Alright, I've simplified this PR to just adding Previous attempt can (for now) be found at master...jfmengels:test:navigation-key-old |
1b7de65 to
5c2a940
Compare
|
This probably closes #24 |
Closes #24
Adds
Test.withNavigationKeywhich provide aBrowser.Navigation.Keyto tests.This functions is designed such that the key can't be used in production code in a way that will have a noticeable effect (one could generate a test in production code, but they can't really do anything with a test).
Without this, the workaround is to define a wrapper type
as well as wrapper functions for
Browser.Navigationfunctions (such aspushUrl) that accept this type, where they'll do nothing if the key is not a real key.Implemention-wise, the key is a simple function with a side-effect and no input (source).
I've introduced a new test key which is a plain noop function, which should be equivalent in all "noticeable" ways (even through
Debug.log).Releasing this functionality will lock the implementation of
elm/browser's key to such a function, at least until it releases a new major version.