-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[Enterprise Search] Telemetry: refactor to Kea logic file #81926
Conversation
try { | ||
TelemetryLogic.actions.sendTelemetry({ action: '', metric: '', product: '' }); | ||
} catch (e) { | ||
expect(e.message).toEqual('Unable to send telemetry'); |
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.
Just a check, could this test pass with a false positive in the case where this action never threw?
Like if someone updated the telemetry method to use post
instead of put
, I think this would still pass... because put
is never called, the action never throws, and this catch is never executed. In that case, this test should fail but it would pass.
Just thinking that something written like the following might be more resilient:
it('throws an error if the telemetry endpoint fails', async () => {
const promise = Promise.reject()
mockHttpValues.http.put.get.mockReturnValue(promise);
let message = "";
TelemetryLogic.actions.sendTelemetry({ action: '', metric: '', product: '' });
try {
await promise;
} catch (e) {
message = e.message;
}
expect(message).toEqual('Unable to send telemetry');
});
Actually, I'm wondering if this is already a false positive. As in, since this is not an async test method, is that catch ever even being triggered?
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.
Ohh super good point about false positives/this not being an async fn! 👍 I like your proposed solution a lot, will definitely implement 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.
You were super on point about it being a false positive! I added an expect.assertions(1)
to the top of the test and confirmed that it had 0 assertions, which means the catch wasn't firing. Credit to this article for the nifty technique:
https://medium.com/@liran.tal/demystifying-jest-async-testing-patterns-b730d4cca4ec#a5c3
@@ -22,6 +23,7 @@ export const mockAllValues = { | |||
...mockFlashMessagesValues, | |||
}; | |||
export const mockAllActions = { | |||
...mockTelemetryActions, |
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 do you add these here if you just end up importing telementry_logic.mock
directly in all of 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.
Because that way you import a single __mocks__/kea.mock.ts
to mock all possible shared logic file values/actions at once instead of having to manually and tediously mock every logic file that you need.
So for example, in product_card.test.tsx:
This file calls both KibanaLogic
and TelemetryLogic
. Because we have this shared kea.mock.ts
helper, we only have to import it once and now the test file gracefully handles all instances of useValues and useActions, returning sane defaults that we can either spy on or override as needed (via our exported mock*Values
/mock*Actions
objs).
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 looks great.
You're right, it seems to greatly simplify things. I had one hesitation about one of your unit tests, but other than that it looks great.
Thanks for doing this.
Also, I did not QA this. Let me know if that is something you'll need. |
- DRYs out need to import/pass http lib - adds product-specific helpers which DRYs out an extra line
- Create reusable mocks - Update overview_logic.ts to use new Kea mock helpers (required for recent_activity.test to pass)
+ update tests
1db25d7
to
41de835
Compare
41de835
to
589d65e
Compare
expect(e.message).toEqual('Unable to send telemetry'); | ||
} | ||
|
||
// Using TelemetryLogic.actions.sendTelemetry causes this test to not work - |
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 mentioned this in a comment in Slack, but repeating it here. I have this weird sense of guilt about this comment, like we're blaming Kea for doing something weird 😭
Actions are like events in Kea, and reducers and listeners are like event handlers.
I would not expect that triggering an event would return a value from an event handler. Therefore, I would not expect that dispatching an action would return a value from a listener.
I mean think about it, there could be multiple event listeners listening for that event, how would you even know which value to return from the action invocation if there are 5 different handlers or listeners running.
The same is true with Kea and Redux.
Kea abstracts some things away from Redux... like when you call an action in Kea, you are invoking it in a way that it appears like a direct invocation of the handler logic, and less like triggering an event:
sendTelemetry()
In Redux, it looks more like the following, which makes it less tempting to expect a return value:
store.dispatch({ type: 'sendTelemetry' })
This is basically a long winded way of saying if you shift your thinking a bit, this seems less like interference.
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.
Thanks for taking the time to write that all out Jason!!! Totally agreed and I found that super helpful / learned a few new things.
589d65e
to
b1fb8eb
Compare
💚 Build SucceededMetrics [docs]@kbn/optimizer bundle module count
async chunks size
History
To update your PR or re-run it, just comment with: |
…82473) * Add new TelemetryLogic helpers - DRYs out need to import/pass http lib - adds product-specific helpers which DRYs out an extra line * Update all previous sendTelemetry fns to use new logic actions * Update unit tests for updated components - Create reusable mocks - Update overview_logic.ts to use new Kea mock helpers (required for recent_activity.test to pass) * Cleanup: Remove old sendTelemetry fn + update tests * [PR feedback] Correctly assert the async thrown error
Summary
This refactors our
sendTelemetry()
utility function (that we primarily used for onClick actions) into a logic action, which allows us to not have to import/pass thehttp
dependency in everysendTelemetry
call.Before:
After:
Hopefully the code speaks for itself and feels clearer / closer to the
<SendTelemetry />
components. It also makes unit tests a little less verbose, as we have a lot more helpers in place for mocking Kea values/actions.Regression QA
All of the following telemetry actions/events should correctly send 200 XHR requests to
api/enterprise_search/stats
:Checklist