• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

validide / javascript-browser-utilities / 13384610121

18 Feb 2025 06:28AM UTC coverage: 100.0%. Remained the same
13384610121

push

github

validide
Bump serialize-javascript and mocha

Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) to 6.0.2 and updates ancestor dependency [mocha](https://github.com/mochajs/mocha). These dependencies need to be updated together.


Updates `serialize-javascript` from 6.0.0 to 6.0.2
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](https://github.com/yahoo/serialize-javascript/compare/v6.0.0...v6.0.2)

Updates `mocha` from 10.2.0 to 10.8.2
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v10.2.0...v10.8.2)

---
updated-dependencies:
- dependency-name: serialize-javascript
  dependency-type: indirect
- dependency-name: mocha
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

162 of 162 branches covered (100.0%)

Branch coverage included in aggregate %.

373 of 373 relevant lines covered (100.0%)

17.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/src/dom/iframe/iframeHttpRequest.ts
1
import { BaseComponent } from '../../contracts/index';
1✔
2
import { generateUniqueId } from '../document/generateIds';
1✔
3
import { getUrlFullPath } from '../document/getUrlFullPath';
1✔
4
import { appendDataToForm } from '../form/appendDataToForm';
1✔
5

6
type LoadHandlerFunctionType = (this: IframeHttpRequest, e: Event) => void;
7
type ResolvePromiseFunctionType<T> = (value: T | PromiseLike<T>) => void;
8
type RejectPromiseFunctionType = (reason: IframeHttpResponse) => void;
9

10
/**
11
 * Configure the request options
12
 *
13
 * @property timeout The maximum request timeout (in milliseconds). The request will be reject as TIMEOUT error if nothing happens in this interval.
14
 * @property redirectTimeout The maximum delay (in milliseconds) between consecutive redirect. If set to zero or less the first response is returned.
15
 */
16
export interface IframeHttpRequestOptions {
17
  timeout: number;
18
  redirectTimeout: number;
19
}
20

21
/**
22
 * The response
23
 *
24
 * @property data The body innerHTML in case of success or an empty string in case of error.
25
 * @property error The error in case of the promise beeing rejected or the null in case of success.
26
 */
27
export interface IframeHttpResponse {
28
  data: string;
29
  error: Error | null;
30
}
31

32
/**
33
 * Make a HTTP request using an iframe
34
 */
35
export class IframeHttpRequest extends BaseComponent {
1✔
36
  private url: string;
37
  private data: Record<string, unknown> | null = null;
19✔
38
  private method = 'GET';
19✔
39
  private options: IframeHttpRequestOptions;
40

41
  private resolvePromise: ResolvePromiseFunctionType<IframeHttpResponse> | null;
42
  private rejectPromise: RejectPromiseFunctionType | null;
43
  private loadHandlerRef: LoadHandlerFunctionType | null;
44
  private wrapperId: string;
45
  private timeoutRef: number;
46
  private redirectTimeoutRef: number;
47
  private called: boolean;
48
  private disposed: boolean;
49

50
  /**
51
   * Default options IframeHttpRequestOptions
52
   */
53
  // eslint-disable-next-line @typescript-eslint/naming-convention
54
  public static DEFAULT_OPTIONS: IframeHttpRequestOptions = {
1✔
55
    timeout: 30 * 1000,
56
    redirectTimeout: 3 * 1000
57
  };
58

59
  /**
60
   * Object constructor
61
   *
62
   * @param window A reference to the window object
63
   * @param url The url to make the request to
64
   * @param data The data to send. Default NULL
65
   * @param method The HTTP method. GET(default) or POST
66
   * @param options The request options IframeHttpRequestOptions. Default null and will use the IframeHttpRequest.DEFAULT_OPTIONS
67
   */
68
  constructor(
69
    window: Window,
70
    url: string,
71
    data: Record<string, unknown> | null = null,
25✔
72
    method = 'GET',
29✔
73
    options: IframeHttpRequestOptions | null = null
32✔
74
  ) {
75
    super(window);
20✔
76

77
    this.validateInput(url, method);
19✔
78
    this.url = url; // might consider defaulting to 'about:blank' as empty url is not allowed for src on iFrames and this is where this will end-up
17✔
79
    // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
80
    this.data = data;
17✔
81
    this.method = method;
17✔
82
    this.options = (Object.assign({}, IframeHttpRequest.DEFAULT_OPTIONS, options) );
17✔
83
    this.resolvePromise = null;
17✔
84
    this.rejectPromise = null;
17✔
85
    this.loadHandlerRef = (e: Event) => this.loadHandler(e);
17✔
86
    this.wrapperId = generateUniqueId(this.getDocument(), 'IframeHttpRequest_wrapper_');
17✔
87
    this.timeoutRef = 0;
17✔
88
    this.redirectTimeoutRef = 0;
17✔
89
    this.called = false;
17✔
90
    this.disposed = false;
17✔
91
  }
92

93
  public sendAsync(): Promise<IframeHttpResponse> {
1✔
94
    if (this.called)
15✔
95
      throw new Error('The "send" method was already called!');
1✔
96

97
    this.called = true;
14✔
98
    this.init();
14✔
99

100
    return this.sendAsyncCore();
14✔
101
  }
102

103
  public dispose(): void {
1✔
104
    if (this.disposed)
24✔
105
      return;
9✔
106

107
    this.disposed = true;
15✔
108
    const win = this.getWindow();
15✔
109
    win.clearTimeout(this.timeoutRef);
15✔
110
    this.timeoutRef = 0;
15✔
111
    win.clearTimeout(this.redirectTimeoutRef);
15✔
112
    this.redirectTimeoutRef = 0;
15✔
113
    const wrapper = this.getDocument().getElementById(this.wrapperId);
15✔
114

115
    if (wrapper) {
15✔
116
      (wrapper.querySelector('iframe') as HTMLIFrameElement).removeEventListener('load', this.loadHandlerRef as LoadHandlerFunctionType, false);
13✔
117
      (wrapper.parentElement as HTMLElement).removeChild(wrapper);
13✔
118
    }
119

120
    this.loadHandlerRef = null;
15✔
121
    this.resolvePromise = null;
15✔
122
    this.rejectPromise = null;
15✔
123
    this.url = '';
15✔
124
    this.method = '';
15✔
125
    this.wrapperId = '';
15✔
126
    super.dispose();
15✔
127
  }
128

129
  private validateInput(url: string, method: string): void {
1✔
130
    if (!url)
19✔
131
      throw new Error('Missing "url" reference.');
1✔
132

133
    switch (method.toUpperCase()) {
18✔
134
      case 'GET':
135
      case 'POST':
136
        break;
17✔
137

138
      default:
139
        throw new Error(`Method not supported "${method}"`);
1✔
140
    }
141
  }
142

143
  private init(): void {
1✔
144
    const iframeId = this.wrapperId + '_iframe';
14✔
145
    const fragment = this.getDocument().createDocumentFragment();
14✔
146
    const wrapper = this.getDocument().createElement('div');
14✔
147

148
    wrapper.id = this.wrapperId;
14✔
149
    wrapper.style.display = 'none';
14✔
150
    wrapper.innerHTML = `<form target="${iframeId}"></form><iframe id="${iframeId}" name="${iframeId}" width="0" height="0" src="about:blank"></iframe>`;
14✔
151

152
    const form = wrapper.querySelector('form') as HTMLFormElement;
14✔
153
    form.action = this.url;
14✔
154
    form.method = this.method;
14✔
155
    form.target = iframeId;
14✔
156
    if (this.data !== null) {
14✔
157
      appendDataToForm(this.data, form);
2✔
158
    }
159

160
    fragment.appendChild(wrapper);
14✔
161
    this.getDocument().body.appendChild(fragment);
14✔
162

163
    const iframe = wrapper.querySelector('iframe') as HTMLIFrameElement;
14✔
164

165
    iframe.addEventListener('load', this.loadHandlerRef as LoadHandlerFunctionType, false);
14✔
166
  }
167

168
  private sendAsyncCore(): Promise<IframeHttpResponse> {
14✔
169
    return new Promise<IframeHttpResponse>((resolve, reject) => {
14✔
170
      this.resolvePromise = resolve;
14✔
171
      this.rejectPromise = reject;
14✔
172

173
      const wrapper = this.getDocument().getElementById(this.wrapperId) as HTMLDivElement;
14✔
174
      try {
14✔
175
        (wrapper.querySelector('form') as HTMLFormElement).submit();
14✔
176
        this.timeoutRef = this.getWindow().setTimeout(() => {
13✔
177
          this.reject(new Error('TIMEOUT'));
1✔
178
        },
179
        this.options.timeout
180
        );
181
      } catch (error) {
182
        this.reject(error as Error);
6✔
183
      }
184
    });
185
  }
186

187
  private loadHandler(event: Event): void {
1✔
188
    this.getWindow().clearTimeout(this.redirectTimeoutRef);
8✔
189
    const allowRedirects = this.options.redirectTimeout > 0;
8✔
190

191
    try {
8✔
192
      const contentWindow = (event.target as HTMLIFrameElement).contentWindow as Window;
8✔
193
      // this should throw if iframe is not accessible due to 'X-Frame-Options'
194
      const targetPath = getUrlFullPath(contentWindow.document, contentWindow.location.href).toLowerCase();
5✔
195
      const desiredPath = getUrlFullPath(contentWindow.document, this.url).toLowerCase();
5✔
196
      const result:IframeHttpResponse = {
5✔
197
        data: contentWindow.document.body.textContent as string,
198
        error: null
199
      };
200

201
      if ((targetPath === desiredPath)) {
5✔
202
        this.resolve(result);
2✔
203
      } else {
204
        if (allowRedirects) {
3✔
205
          this.schedulePromiseResolve(result);
1✔
206
        } else {
207
          this.resolve(result);
2✔
208
        }
209
      }
210
    } catch (error) {
211
      if (allowRedirects) {
3✔
212
        this.schedulePromiseResolve({
2✔
213
          data: '',
214
          // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
215
          error: error as Error
216
        });
217
      } else {
218
        this.reject(error as Error);
1✔
219
      }
220
    }
221
  }
222

223
  private schedulePromiseResolve(result:IframeHttpResponse): void {
3✔
224
    const win = this.getWindow();
3✔
225
    win.clearTimeout(this.redirectTimeoutRef);
3✔
226
    this.redirectTimeoutRef = win.setTimeout(() => { this.resolve(result); }, this.options.redirectTimeout);
3✔
227
  }
228

229
  private resolve(value: IframeHttpResponse): void {
1✔
230
    (this.resolvePromise as ResolvePromiseFunctionType<IframeHttpResponse>)(value);
5✔
231
    this.dispose();
5✔
232
  }
233

234
  private reject(error: Error): void {
1✔
235
    (this.rejectPromise as RejectPromiseFunctionType)({ data: '', error: error });
8✔
236
    this.dispose();
3✔
237
  }
238
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc