test: Improve Cypress error messages when Linode API errors occur#9777
Conversation
| // When a Linode APIv4 schema validation error occurs, an array of `APIError` | ||
| // objects is thrown rather than a typical `Error` type. |
There was a problem hiding this comment.
I'm not sure if this is intended, but I think this is the source of the behavior.
There was a problem hiding this comment.
Yeah our error handling/normalization between Cloud Manager and api-v4 is a bit wacky
Are you saying that api-v4 throws Axios errors normally, but APIError[] if it is a schema validation error?
There was a problem hiding this comment.
@bnussman-akamai Yeah, that's my understanding -- an actual error from the API (like a 400 response, etc.) is an AxiosError<{ errors: APIError[] }> whereas schema validation errors are just APIError[] (in the schema validation Before screenshot in the PR description, Cypress even calls out that it's not an Error that was thrown, but an array).
I don't really think it's a big deal, and even if this is a mistake I'm guessing we're in too deep now, just a little thing that caught me by surprise while working on this!
There was a problem hiding this comment.
Yeah, I've noticed the same and agree we're in too deep 😅
On a semi related note, @jcallahan-akamai and I were just recently talking about how we can't see status codes because of our APIError[] pattern. 😣
…occurs in Cypress test
| // When a Linode APIv4 schema validation error occurs, an array of `APIError` | ||
| // objects is thrown rather than a typical `Error` type. |
There was a problem hiding this comment.
Yeah our error handling/normalization between Cloud Manager and api-v4 is a bit wacky
Are you saying that api-v4 throws Axios errors normally, but APIError[] if it is a schema validation error?
coliu-akamai
left a comment
There was a problem hiding this comment.
Thanks Joe -- error messages were definitely a lot more helpful!
✅ confirmed error message for a weak root_password
✅ confirmed error message for a null root_password
| errors: APIError[]; | ||
| }; | ||
|
|
||
| /** |
There was a problem hiding this comment.
Very well documented, makes it easy to follow, thank you!
| * | ||
| * @returns `true` if `e` is a Linode API v4 request error. | ||
| */ | ||
| const isLinodeApiError = (e: any): e is AxiosError<LinodeApiV4Error> => { |
There was a problem hiding this comment.
Just out of curiosity, do you know why we use LinodeApiV4Error instead of just using APIError[]?
There was a problem hiding this comment.
LinodeApiV4Error is defined towards the top of this file, and it's just an alias for any object with an errors property containing an array of APIErrors.
APIError[] alone isn't applicable here because the error type we're checking for is an Axios error object which contains an HTTP response containing the errors. AxiosError<APIError[]> also isn't applicable since the response body isn't just an array of APIError objects, it's an object itself which contains an errors property.
This could have been expressed as AxiosError<{ errors: APIError[] }>, but I opted to define a type for this within the module because I expected to have to refer to that type a lot (in the end I really didn't).
| // and request URL when applicable. | ||
| const summary = !!e.response?.status | ||
| ? `Linode APIv4 request failed with status code ${e.response.status}` | ||
| : `Linode APIv4 request failed`; |
There was a problem hiding this comment.
Do you think it would be helpful here to add Linode APIv4 request failed **without status code** for the false condition just so the user knows there wasn't one so they dont spend time looking for it?
| const errorDetails = e.response!.data.errors.map((error: APIError) => { | ||
| return error.field | ||
| ? `- ${error.reason} (field '${error.field}')` | ||
| : `- ${error.reason}`; |
There was a problem hiding this comment.
Same here.
Its up to you though, I dont know if the status code and the error field are important, if not, feel free to disregard my comments.
| const requestInfo = | ||
| !!e.request?.responseURL && !!e.config.method | ||
| ? `\nRequest: ${e.config.method.toUpperCase()} ${e.request.responseURL}` | ||
| : ''; |
| if (isValidationError(e)) { | ||
| // Validation errors do not contain any additional context (request URL, payload, etc.). | ||
| // Show the validation error messages instead. | ||
| const multipleErrors = e.length > 1; |
There was a problem hiding this comment.
Another way to do this:
const errorPlurality = e.length > 1 ? 'errors' : 'error';
const summary = `Request failed with Linode schema validation ${errorPlurality}`;| : 'Request failed with Linode schema validation error'; | ||
|
|
||
| // Format, accounting for 0, 1, or more errors. | ||
| const validationErrorMessage = multipleErrors |
There was a problem hiding this comment.
Since both conditions are pretty similar, another way to do it is:
const prefix = multipleErrors ? '- ' : '';
// Format, accounting for 0, 1, or more errors.
const validationErrorMessage = e
.map((error) =>
error.field
? `${prefix}${error.reason} (field '${error.field}')`
: `${prefix}${error.reason}`
)
.join('\n');There was a problem hiding this comment.
or tbh adding a hyphen before any error, even if its just one single error might still help readability. If this is the case, you wouldn't need the conditional.
Up to you though!


Description 📝
This improves error handling when a Linode API error or validation error occurs during a Cypress test by adding additional information to the command log. This is intended to help troubleshoot test failures.
(Meta: Not sure if this is better suited as
testorrefactortype, happy to change this torefactorif we think that's more applicable)Changes 🔄
cy.deferPreview 📷
How to test 🧪
yarn cy:debug:linodeRequestincypress/support/util/linode-utils.ts(line 10) to contain a short/weakroot_passpropertyyarn cy:debugand selectrescue-linode.spec.tsin the UIAs an Author I have considered 🤔
Check all that apply