1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | import { jest, it, describe, expect } from "@jest/globals";
|
23 | import {
|
24 | postRedirectUrlToParent,
|
25 | redirectInIframe,
|
26 | setupIframeListener,
|
27 | } from "../src/iframe";
|
28 |
|
29 | describe("redirectInIframe", () => {
|
30 | it("creates an iframe with the appropriate attributes and performs the redirection in it", () => {
|
31 | redirectInIframe("http://some.iri");
|
32 |
|
33 | const iframe = document.getElementsByTagName("iframe")[0];
|
34 |
|
35 |
|
36 |
|
37 | expect(iframe).not.toBeUndefined();
|
38 |
|
39 |
|
40 | expect(iframe.getAttribute("hidden")).toBe("true");
|
41 | expect(iframe.getAttribute("sandbox")).toBe(
|
42 | "allow-scripts allow-same-origin"
|
43 | );
|
44 | expect(iframe.getAttribute("src")).toBe("http://some.iri");
|
45 | });
|
46 | });
|
47 |
|
48 | describe("setupIframeListener", () => {
|
49 | const setupDom = (originMatch: boolean, sourceMatch: boolean) => {
|
50 | jest.spyOn(window, "location", "get").mockReturnValue({
|
51 |
|
52 | origin: originMatch ? "" : "https://some.other/origin",
|
53 | } as Location);
|
54 |
|
55 |
|
56 | redirectInIframe("http://some.iri");
|
57 | const iframe = document.getElementsByTagName("iframe")[0];
|
58 | jest
|
59 | .spyOn(iframe, "contentWindow", "get")
|
60 | .mockReturnValue((sourceMatch ? null : ({} as unknown)) as Window);
|
61 | };
|
62 |
|
63 | const mockEventListener = () => {
|
64 | let messageReceived = false;
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 | const blockingPromise = new Promise(async (resolve) => {
|
72 | while (!messageReceived) {
|
73 |
|
74 |
|
75 | await new Promise((resolveSleep) => setTimeout(resolveSleep, 100));
|
76 | }
|
77 | resolve(undefined);
|
78 | });
|
79 | window.addEventListener("message", () => {
|
80 | messageReceived = true;
|
81 | });
|
82 | return blockingPromise;
|
83 | };
|
84 |
|
85 | it("ignores message from iframes on different origins", async () => {
|
86 | const callback = jest.fn();
|
87 | const blockingPromise = mockEventListener();
|
88 | setupDom(false, true);
|
89 |
|
90 | setupIframeListener(callback as any);
|
91 |
|
92 | window.postMessage(
|
93 | {
|
94 | redirectUrl: "http://some.redirect/url",
|
95 | },
|
96 | "http://localhost"
|
97 | );
|
98 |
|
99 | await blockingPromise;
|
100 |
|
101 | expect(callback).not.toHaveBeenCalled();
|
102 | });
|
103 |
|
104 | it("ignores messages from iframes with an unknown source", async () => {
|
105 | const callback = jest.fn();
|
106 | const blockingPromise = mockEventListener();
|
107 | setupDom(true, false);
|
108 |
|
109 | setupIframeListener(callback as any);
|
110 |
|
111 | window.postMessage(
|
112 | {
|
113 | redirectUrl: "http://some.redirect/url",
|
114 | },
|
115 | "http://localhost"
|
116 | );
|
117 |
|
118 | await blockingPromise;
|
119 |
|
120 | expect(callback).not.toHaveBeenCalled();
|
121 | });
|
122 |
|
123 | it("ignores messages from valid iframes but with an unexpected structure", async () => {
|
124 | const callback = jest.fn();
|
125 | const blockingPromise = mockEventListener();
|
126 | setupDom(true, true);
|
127 |
|
128 | setupIframeListener(callback as any);
|
129 |
|
130 | window.postMessage(
|
131 | {
|
132 |
|
133 | someUnknownkey: "some value",
|
134 | },
|
135 | "http://localhost"
|
136 | );
|
137 |
|
138 | await blockingPromise;
|
139 |
|
140 | expect(callback).not.toHaveBeenCalled();
|
141 | });
|
142 |
|
143 | it("calls the given callback", async () => {
|
144 | const callback = jest.fn();
|
145 | const blockingPromise = mockEventListener();
|
146 | setupDom(true, true);
|
147 |
|
148 | setupIframeListener(callback as any);
|
149 |
|
150 | window.postMessage(
|
151 | {
|
152 | redirectUrl: "http://some.redirect/url",
|
153 | },
|
154 | "http://localhost"
|
155 | );
|
156 |
|
157 | await blockingPromise;
|
158 |
|
159 | expect(callback).toHaveBeenCalled();
|
160 | });
|
161 |
|
162 | it("cleans up the iframe after the message is received", async () => {
|
163 | const callback = jest.fn();
|
164 | const blockingPromise = mockEventListener();
|
165 | setupDom(true, true);
|
166 |
|
167 | setupIframeListener(callback as any);
|
168 |
|
169 | expect(document.getElementsByTagName("iframe")[0]).not.toBeUndefined();
|
170 |
|
171 | window.postMessage(
|
172 | {
|
173 | redirectUrl: "http://some.redirect/url",
|
174 | },
|
175 | "http://localhost"
|
176 | );
|
177 |
|
178 | await blockingPromise;
|
179 |
|
180 |
|
181 | expect(document.getElementsByTagName("iframe")[0]).toBeUndefined();
|
182 | });
|
183 | });
|
184 |
|
185 | describe("postRedirectUrlToParent", () => {
|
186 | it("posts a message to the parent window with the provided url", () => {
|
187 | const spyPost = jest.spyOn(window.top, "postMessage");
|
188 | jest.spyOn(window, "location", "get").mockReturnValue({
|
189 | origin: "https://some.origin/",
|
190 | } as unknown as Location);
|
191 | postRedirectUrlToParent("https://some.redirect.url/");
|
192 | expect(spyPost).toHaveBeenCalledWith(
|
193 | { redirectUrl: "https://some.redirect.url/" },
|
194 | "https://some.origin/"
|
195 | );
|
196 | });
|
197 | });
|