-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make run button functional #25
Make run button functional #25
Conversation
This was necessitated because the nuxt test utils don't seem to have a way to pass through a body.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #25 +/- ##
==========================================
+ Coverage 58.70% 61.02% +2.32%
==========================================
Files 26 32 +6
Lines 724 798 +74
Branches 63 74 +11
==========================================
+ Hits 425 487 +62
- Misses 290 300 +10
- Partials 9 11 +2 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work !!! looks clean and works great!!!!!! just left some minor comments
components/ParameterForm.vue
Outdated
formSubmitting.value = true; | ||
const response = await $fetch<NewScenarioData>("/api/scenarios", { | ||
method: "POST", | ||
query: { // Using query instead of body because I couldn't work out how to send a body in the integration test. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should defs use body here... your code shouldn't change to accommodate the tests...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a reason to prefer body over query; is there one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Semantically the use of query params is incorrect here... they are more for filtering, sorting / picking certain ids... when doing post request and sending data its semantically correct to use a body
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed - structured data should be sent in a POST body not in a single qs param, that's our standard approach for submitting a run. It's easier to validate, more readable etc.
Must be some way of getting the integration test to accept a body - if not, we might need a different integration test approach!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried for some time to use the nuxt test utils to pass through a body, but there didn't seem to be a way.
To be clear, the R API is still receiving a body, it's only the web app that's taking a query parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried again today with a fresh brain and am making headway :)
components/ParameterForm.vue
Outdated
}); | ||
|
||
const appStore = useAppStore(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should move this at top with all variable definitions... nice to keep nicely separated with methods
components/ParameterForm.vue
Outdated
|
||
if (response) { | ||
const { runId } = response; | ||
navigateToData.value = `/scenarios/${runId}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
navigateToData.value = `/scenarios/${runId}`; | |
await navigateTo(`/scenarios/${runId}`) |
unless reason for assignment before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is for testing only, because I couldn't get a spy on navigateTo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, this seems like an issue with the testing approach that isn't ideally solved by putting unnecessary stuff in the code. And if we really really need the crud it ought to be commented as such.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had the comment in the test file - there are two other places it could be, here, and by the data attribute itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think our only option besides this is just not to test the form submission as part of component tests. That means we would rely only on e2e tests for the form submission. But since our e2e test doesn't mock the R API, we wouldn't be able to verify that the correct parameters were passed to it by the component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, until such time as it can test the result page, I suppose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Emma found this solution: #29
server/api/scenarios.post.ts
Outdated
|
||
export default defineRApiEventHandler( | ||
async (event): Promise<NewScenarioResponse> => { | ||
const query = getQuery(event); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as per prev comment should be reading body not query params
server/api/scenarios.post.ts
Outdated
|
||
const modelParameters = JSON.parse(query.parameters as string) as ParameterDict; | ||
|
||
// Delegate to runScenario so that the logic can be unit-tested. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont need this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've moved this comment - which was getting duplicated for each api endpoint - into a new .md "API design decisions.md"
server/handlers/scenarios.ts
Outdated
|
||
const rApiNewScenarioEndpoint = "/scenario/run"; | ||
|
||
// Send a request to the R API for a job run. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont neeed comments again.. the code you wrote is clean and understandable
stores/appStore.ts
Outdated
initializeAppState(): void { | ||
this.largeScreen = true; | ||
}, | ||
setScreenSize(bool: boolean): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be setScreenSizeToLarge
.. also function param should be descrptive .. eg. isLargeScreen rather than bool
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good points.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have changed the function param to be called large, otherwise kept this the same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great stuff, good to get the store in.
Seems like some of the testing was a headache, which I'm probably adding to with some of these comments.. I think it would be really good to avoid some of these test-related-code-polluting additions if possible. Have made a couple of suggestions which you may have already tried. Happy to have a look at any of these if you're sick of bashing against them yourself - can't guarantee to get anywhere, as this is mostly all new to me too, but I think it's worth solving these now if we can, and can avoid proliferation.
@@ -1,9 +1,11 @@ | |||
<template> | |||
<div> | |||
<div v-show="pageMounted"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this actually necessary? A Playwright timing issue? It should be possible to write Playwright tests so that they wait for a condition to become true. I think I'd rather have messy tests than somewhat inexplicable properties of the code that are only there because of test behaviour. If it really isn't then let's have a comment explaining it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Luckily, this problem disappears next PR, because by then we can wait for version data as our signal that everything is mounted.
components/ParameterForm.vue
Outdated
<CForm | ||
v-if="props.metadata && formData" | ||
class="inputs" | ||
:data-test="JSON.stringify(formData)" | ||
role="form" | ||
:data-test-form-data="JSON.stringify(formData)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, this feels like it should be avoidable - if it's for component testing, can't we just examine the values in the rendered elements?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the dependency on this from tests. In the component test, I think the test relying on this was redundant anyway.
components/ParameterForm.vue
Outdated
@@ -36,6 +38,7 @@ | |||
autocomplete="off" | |||
:label="option.label" | |||
:value="option.id" | |||
:disabled="!pageMounted" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't be shown if page isn't mounted, so not sure this is needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed in next PR.
components/ParameterForm.vue
Outdated
@@ -49,7 +52,8 @@ | |||
:id="parameter.id" | |||
v-model="formData[parameter.id]" | |||
:aria-label="parameter.label" | |||
class="form-select" :class="[largeScreen ? 'form-select-lg' : '']" | |||
class="form-select" :class="[screenIsLarge ? 'form-select-lg' : '']" | |||
:disabled="!pageMounted" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
..ditto..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed in next PR.
components/ParameterForm.vue
Outdated
|
||
if (response) { | ||
const { runId } = response; | ||
navigateToData.value = `/scenarios/${runId}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, this seems like an issue with the testing approach that isn't ideally solved by putting unnecessary stuff in the code. And if we really really need the crud it ought to be commented as such.
appStore.initializeAppState(); | ||
|
||
const setScreenSize = () => { | ||
// As this function uses window, it can only be run client-side, so we have to pass the $pinia instance |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// As this function uses window, it can only be run client-side, so we have to pass the $pinia instance | |
// As this function uses window, it can only be run client-side (post-setup), so we have to pass the $pinia instance |
The linkage between these clauses wasn't immediately obvious to me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went through an adventure since writing this, submitted a PR to clarify the docs of the nuxt pinia module, and eventually the maintainer let me know that in fact "you don't need to pass the pinia instance within setup"
vuejs/pinia#2766
As such, the next PR removes this, here: https://github.com/jameel-institute/daedalus-web-app/pull/27/files#diff-433a9abd4ca70520f69fc064c160bb093918ce26dda6087a8bbfdbe2895d1944
tests/e2e/runAnalysis.spec.ts
Outdated
await page.click('div[aria-label="Advance vaccine investment"] label[for="medium"]'); | ||
await page.click('div[aria-label="Advance vaccine investment"] label[for="low"]'); | ||
|
||
await expect(page.getByRole("form")).toHaveAttribute("data-test-form-data", "{\"country\":\"United States\",\"pathogen\":\"influenza-1957\",\"response\":\"elimination\",\"vaccine\":\"low\"}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should be resorting to hidden attribute tests in e2e tests of forms - we should just be able to look at the values of the inputs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can be confident that the values of the inputs are what we just selected. I think the test to do for the e2e would be to wait until the result page is implemented, and test that the displayed parameters are correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hang on, that doesn't look like the line (I thought) this was a comment for anymore, I'm pretty sure this was about the stringified JSON - or I thought it was!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still looks like it's commenting on the stringified JSON to me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I'm just blind as a 🦇
tests/integration/rApi.spec.ts
Outdated
it("returns a successful response when the mock server responds successfully", async () => { | ||
const queryString = `parameters={"mockoonResponse":"successful","country":"Thailand","pathogen":"sars-cov-1","response":"no_closure","vaccine":"none"}`; | ||
|
||
const response = await nuxtTestUtilsFetch(`/api/scenarios?${queryString}`, { method: "POST" }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the test that was a problem for POSTing a body? I'm not completely sure, but it looks to me like the nuxt test utils uses ofetch: https://github.com/nuxt/test-utils/blob/f7afe52243a044694402fa16ddf51e80626133ab/src/core/server.ts#L4
..and that you should be able to include a POST body in the body
option:
https://github.com/unjs/ofetch?tab=readme-ov-file#%EF%B8%8F-json-body
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My problem had been that I was trying to pass objects into the 'body' property that just got lost in the post, as it were. Strings and objects evaporated. I eventually solved this by finding that I could wrap the parameters in a FormData
object, which fetch
does accept, and decompose that at the other end.
// I couldn't find a way to spy on the navigateTo function, so this is a second-best test that we | ||
// at least construct the correct path to navigate to. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you try stubGlobal or mockNuxtImport?
https://stackoverflow.com/questions/77827093/how-to-check-if-navigateto-was-called-in-nuxt-js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, let's not hold up this branch, but it would be really good to find a way to mock global methods, and it seems really weird if the alleged mocking capacities of vitest/nuxt test utils aren't going to let us to that. Tbh if that proves impossible personally I'd rather not clutter up the code with extra props for the sake of unit tests, as long as the navigation etc gets tested in e2e tests. These components are going to be busy and confusing enough as it is, without a bunch of extra stuff that doesn't really need to be there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think at some point we're going to have to work out how to POST JSON with Nuxt tests, as it really ought to be possible 😆 It's not terrible to post form values as form data but it would be nice to have the option not to..
See previous comment on the mocking navigate controversy - I'd rather take out the props that are just there for unit tests if it's really not possible to mock, and make sure the navigration is tested in e2e. I might have a fiddle around with the mocking, maybe I'll stumble on some other way of doing it... but that can go in another branch.
components/ParameterForm.vue
Outdated
const formDataObject = new FormData(); | ||
Object.entries(formData.value).forEach(([key, value]) => { | ||
formDataObject.append(key, value.toString()); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd assumed that we were just going to post JSON in the same format that the R API accepts and pass it straight through? You're saying that doesn't work (in the tests), that ordinary objects get turned into... what? Completely empty body?
It's not something to do with needing to set the content type is it? Does it default to POSTing form encoded data so it barfs on plain objects but accepts FormData? Hm, the ofetch docs suggest it should deal with that transparently and add the header itself, but it is supicious... Could try setting that header explicitly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have found something that appears to be working - JSON stringifying the body (in the test). The oftech docs claimed that this happened automatically, but evidently not 😕
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that still it behaves differently under test than in real life, so I had to add in a check for whether the body was being received as an object (as in real life, and in e2e tests) or a JSON parsable string (as in the integration test). I hope this is tolerable, as the price for moving away from FormData.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed. maybe try adding header Content-Type: application/json
when doing the POST.
This seemed to work for me: https://github.com/jameel-institute/daedalus-web-app/pull/29/files# |
6530321
to
2a03920
Compare
…uts-rebasing-onto-front-end-brach-test-fix fix integration tests
Jidea 57 mock navigate to 2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK this one's cooked as far as I'm concerned!
You can test this locally by seeing if clicking the run button takes you to a page showing you the dummy run id.