diff options
Diffstat (limited to 'spec/frontend/lib/utils/rails_ujs_spec.js')
-rw-r--r-- | spec/frontend/lib/utils/rails_ujs_spec.js | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/spec/frontend/lib/utils/rails_ujs_spec.js b/spec/frontend/lib/utils/rails_ujs_spec.js new file mode 100644 index 00000000000..00c29b72e73 --- /dev/null +++ b/spec/frontend/lib/utils/rails_ujs_spec.js @@ -0,0 +1,78 @@ +import { setHTMLFixture } from 'helpers/fixtures'; +import waitForPromises from 'helpers/wait_for_promises'; + +beforeAll(async () => { + // @rails/ujs expects jQuery.ajaxPrefilter to exist if jQuery exists at + // import time. This is only a problem in tests, since we expose jQuery + // globally earlier than in production builds. Work around this by pretending + // that jQuery isn't available *before* we import @rails/ujs. + delete global.jQuery; + + const { initRails } = await import('~/lib/utils/rails_ujs.js'); + initRails(); +}); + +function mockXHRResponse({ responseText, responseContentType } = {}) { + jest + .spyOn(global.XMLHttpRequest.prototype, 'getResponseHeader') + .mockReturnValue(responseContentType); + + jest.spyOn(global.XMLHttpRequest.prototype, 'send').mockImplementation(function send() { + requestAnimationFrame(() => { + Object.defineProperties(this, { + readyState: { value: XMLHttpRequest.DONE }, + status: { value: 200 }, + response: { value: responseText }, + }); + this.onreadystatechange(); + }); + }); +} + +// This is a test to make sure that the patch-package patch correctly disables +// script execution for data-remote attributes. +it('does not perform script execution via data-remote', async () => { + global.scriptExecutionSpy = jest.fn(); + + mockXHRResponse({ + responseText: 'scriptExecutionSpy();', + responseContentType: 'application/javascript', + }); + + setHTMLFixture(` + <a href="/foo/evil.js" + data-remote="true" + data-method="get" + data-type="script" + data-testid="evil-link" + >XSS</a> + `); + + const link = document.querySelector('[data-testid="evil-link"]'); + const ajaxSuccessSpy = jest.fn(); + link.addEventListener('ajax:success', ajaxSuccessSpy); + + link.click(); + + await waitForPromises(); + + // Make sure Rails ajax machinery finished working as expected to avoid false + // positives + expect(ajaxSuccessSpy).toHaveBeenCalledTimes(1); + + // If @rails/ujs has been patched correctly, this next assertion should pass. + // + // Because it's asserting something didn't happen, it is possible for it to + // pass for the wrong reason. So, to verify that this test correctly fails + // when @rails/ujs has not been patched, run: + // + // yarn patch-package --reverse + // + // And then re-run this test. The spy should now be called, and correctly + // fail the test. + // + // To restore the patch(es), run: + // + // yarn install + expect(global.scriptExecutionSpy).not.toHaveBeenCalled(); +}); |