diff --git a/app/api/utils/specs/supertestExtensions.spec.ts b/app/api/utils/specs/supertestExtensions.spec.ts new file mode 100644 index 0000000000..05b984495d --- /dev/null +++ b/app/api/utils/specs/supertestExtensions.spec.ts @@ -0,0 +1,29 @@ +// @ts-ignore +import Test from 'supertest/lib/test'; +import { extendSupertest } from '../supertestExtensions'; + +describe('assertStatus', () => { + const mockError = new Error('mock error'); + const assertStatusMock = jest.fn().mockReturnValue(mockError); + beforeEach(() => { + Test.prototype._assertStatus = assertStatusMock; + extendSupertest(); + }); + + it('should use the original method', () => { + const result = Test.prototype._assertStatus(0, {}); + expect(assertStatusMock).toHaveBeenCalledWith(0, {}); + expect(result).toBe(mockError); + }); + + describe('when the assert fails', () => { + it('should add the body for a 400 code', async () => { + const result = Test.prototype._assertStatus(200, { + status: 400, + text: JSON.stringify('validation error'), + }); + await expect(result.message).toMatch('validation error'); + expect(result.stack).toBeUndefined(); + }); + }); +}); diff --git a/app/api/utils/supertestExtensions.ts b/app/api/utils/supertestExtensions.ts new file mode 100644 index 0000000000..eefa006528 --- /dev/null +++ b/app/api/utils/supertestExtensions.ts @@ -0,0 +1,24 @@ +import { Response } from 'supertest'; +// @ts-ignore +import Test from 'supertest/lib/test'; + +function extractStatusDebugInfo(res: Response): string { + try { + return JSON.stringify(JSON.parse(res.text), null, 2); + } catch (e) { + return res.text; + } +} + +export function extendSupertest() { + const { _assertStatus } = Test.prototype; + + Test.prototype._assertStatus = function extendedAssertStatus(status: number, res: Response) { + const err: Error = _assertStatus(status, res); + if (err) { + err.message += `\n ${extractStatusDebugInfo(res)}`; + delete err.stack; + } + return err; + }; +} diff --git a/app/api/utils/testingRoutes.ts b/app/api/utils/testingRoutes.ts index 0f5f32030a..2907007e35 100644 --- a/app/api/utils/testingRoutes.ts +++ b/app/api/utils/testingRoutes.ts @@ -4,6 +4,9 @@ import { Response as SuperTestResponse } from 'supertest'; import errorHandlingMiddleware from 'api/utils/error_handling_middleware'; import languageMiddleware from 'api/utils/languageMiddleware'; +import { extendSupertest } from './supertestExtensions'; + +extendSupertest(); const iosocket = { emit: jasmine.createSpy('emit') }; diff --git a/app/react/Attachments/components/Attachment.js b/app/react/Attachments/components/Attachment.js index 45f261d046..a4f37e0f67 100644 --- a/app/react/Attachments/components/Attachment.js +++ b/app/react/Attachments/components/Attachment.js @@ -74,7 +74,7 @@ export class Attachment extends Component { this.toggleDropdown = this.toggleDropdown.bind(this); this.handleClickOutside = this.handleClickOutside.bind(this); this.copyToClipboard = this.copyToClipboard.bind(this); - this.myRef = React.createRef(); + this.attachmentActionsRef = React.createRef(); this.onRenameSubmit = this.onRenameSubmit.bind(this); this.toggleRename = this.toggleRename.bind(this); } @@ -131,7 +131,10 @@ export class Attachment extends Component { } handleClickOutside(e) { - if (!this.myRef.current.contains(e.target)) { + if ( + this.attachmentActionsRef.current && + !this.attachmentActionsRef.current.contains(e.target) + ) { this.setState({ dropdownMenuOpen: false }); } } @@ -216,7 +219,7 @@ export class Attachment extends Component { className="dropdown-menu dropdown-menu-right" aria-labelledby="attachment-dropdown-actions" style={{ display: this.state.dropdownMenuOpen ? 'block' : 'none' }} - ref={this.myRef} + ref={this.attachmentActionsRef} >