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

prebid / Prebid.js / 17552805458

08 Sep 2025 01:40PM UTC coverage: 96.25% (+0.003%) from 96.247%
17552805458

push

github

47e5f4
prebidjs-release
Increment version to 10.11.0-pre

39744 of 48861 branches covered (81.34%)

197557 of 205253 relevant lines covered (96.25%)

124.87 hits per line

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

93.75
/test/spec/creative/crossDomainCreative_spec.js
1
import {renderer} from '../../../creative/crossDomain.js';
2
import {
3
  ERROR_EXCEPTION,
4
  EVENT_AD_RENDER_FAILED, EVENT_AD_RENDER_SUCCEEDED,
5
  MESSAGE_EVENT,
6
  MESSAGE_REQUEST,
7
  MESSAGE_RESPONSE
8
} from '../../../creative/constants.js';
9

10
describe('cross-domain creative', () => {
1✔
11
  const ORIGIN = 'https://example.com';
1✔
12
  let win, top, renderAd, messages, mkIframe, consoleErrorStub;
13

14
  beforeEach(() => {
1✔
15
    consoleErrorStub = sinon.stub(console, 'error');
16✔
16
    messages = [];
16✔
17
    mkIframe = sinon.stub();
16✔
18
    top = {
16✔
19
      frames: {}
20
    };
21
    top.top = top;
16✔
22
    win = {
16✔
23
      top,
24
      frames: {},
25
      document: {
26
        body: {
27
          appendChild: sinon.stub(),
28
        },
29
        createElement: sinon.stub().callsFake(tagname => {
30
          switch (tagname.toLowerCase()) {
1!
31
            case 'a':
32
              return document.createElement('a')
×
33
            case 'iframe': {
34
              return mkIframe();
1✔
35
            }
36
          }
37
        })
38
      },
39
      parent: {
40
        parent: top,
41
        frames: {'__pb_locator__': {}},
42
        postMessage: sinon.stub().callsFake((payload, targetOrigin, transfer) => {
43
          messages.push({payload: JSON.parse(payload), targetOrigin, transfer});
25✔
44
        })
45
      }
46
    };
47
    renderAd = (...args) => renderer(win)(...args);
16✔
48
  })
49

50
  afterEach(() => {
1✔
51
    consoleErrorStub.restore();
16✔
52
  })
53

54
  function waitFor(predicate, timeout = 1000) {
11!
55
    let timedOut = false;
11✔
56
    return new Promise((resolve, reject) => {
11✔
57
      const to = setTimeout(() => {
11✔
58
        timedOut = true;
×
59
        reject(new Error('timeout'))
×
60
      }, timeout)
61
      resolve = (orig => () => { clearTimeout(to); orig() })(resolve);
11✔
62
      function check() {
63
        if (!timedOut) {
11✔
64
          setTimeout(() => {
11✔
65
            if (predicate()) {
11!
66
              resolve()
11✔
67
            } else check();
×
68
          }, 50)
69
        }
70
      }
71
      check();
11✔
72
    })
73
  }
74

75
  it('derives postMessage target origin from pubUrl ', () => {
1✔
76
    renderAd({pubUrl: 'https://domain.com:123/path'});
1✔
77
    expect(messages[0].targetOrigin).to.eql('https://domain.com:123')
1✔
78
  });
79

80
  describe('when there are multiple ancestors', () => {
1✔
81
    let target;
82
    beforeEach(() => {
1✔
83
      target = win.parent;
3✔
84
      win.parent = {
3✔
85
        top,
86
        frames: {},
87
        parent: {
88
          ...target,
89
          parent: {
90
            top,
91
            frames: {'__pb_locator__': {}},
92
            parent: {
93
              top,
94
              frames: {}
95
            },
96
          }
97
        }
98
      }
99
    })
100
    Object.entries({
1✔
101
      'throws': () => { throw new DOMException() },
×
102
      'does not throw': () => ({})
×
103
    }).forEach(([t, getFrames]) => {
2✔
104
      describe(`when an ancestor ${t}`, () => {
2✔
105
        beforeEach(() => {
2✔
106
          Object.defineProperty(win.parent.parent.parent.parent, 'frames', {get: getFrames})
2✔
107
        })
108
        it('posts message to the first ancestor with __pb_locator__ child', () => {
2✔
109
          renderAd({pubUrl: 'https://www.example.com'});
2✔
110
          expect(messages.length).to.eql(1);
2✔
111
        });
112
      })
113
    })
114
    it('posts to first restricted parent, if __pb_locator__ cannot be found', () => {
1✔
115
      Object.defineProperty(win.parent.parent.parent, 'frames', {
1✔
116
        get() {
117
          throw new DOMException();
×
118
        }
119
      });
120
      renderAd({pubUrl: 'https://www.example.com'});
1✔
121
      expect(messages.length).to.eql(1);
1✔
122
    })
123
  })
124

125
  it('generates request message with adId and clickUrl', () => {
1✔
126
    renderAd({adId: '123', clickUrl: 'https://click-url.com', pubUrl: ORIGIN});
1✔
127
    expect(messages[0].payload).to.eql({
1✔
128
      message: MESSAGE_REQUEST,
129
      adId: '123',
130
      options: {
131
        clickUrl: 'https://click-url.com'
132
      }
133
    })
134
  });
135

136
  it('runs scripts inserted through iframe srcdoc', (done) => {
1✔
137
    const iframe = document.createElement('iframe');
1✔
138
    iframe.setAttribute('srcdoc', '<script>window.ran = true;</script>');
1✔
139
    iframe.onload = function () {
1✔
140
      expect(iframe.contentWindow.ran).to.be.true;
1✔
141
      done();
1✔
142
    }
143
    document.body.appendChild(iframe);
1✔
144
  })
145

146
  describe('listens and', () => {
1✔
147
    function reply(msg, index = 0) {
11✔
148
      messages[index].transfer[0].postMessage(JSON.stringify(msg));
11✔
149
    }
150

151
    it('ignores messages that are not a prebid response message', () => {
1✔
152
      renderAd({adId: '123', pubUrl: ORIGIN});
1✔
153
      reply({adId: '123', ad: 'markup'});
1✔
154
      sinon.assert.notCalled(mkIframe);
1✔
155
    })
156

157
    it('signals AD_RENDER_FAILED on exceptions', () => {
1✔
158
      mkIframe.callsFake(() => { throw new Error('error message') });
1✔
159
      renderAd({adId: '123', pubUrl: ORIGIN});
1✔
160
      reply({message: MESSAGE_RESPONSE, adId: '123', ad: 'markup'});
1✔
161
      return waitFor(() => messages[1]?.payload).then(() => {
1✔
162
        expect(messages[1].payload).to.eql({
1✔
163
          message: MESSAGE_EVENT,
164
          adId: '123',
165
          event: EVENT_AD_RENDER_FAILED,
166
          info: {
167
            reason: ERROR_EXCEPTION,
168
            message: 'error message'
169
          }
170
        })
171
      })
172
    });
173

174
    describe('renderer', () => {
1✔
175
      beforeEach(() => {
1✔
176
        win.document.createElement.callsFake(document.createElement.bind(document));
8✔
177
        win.document.body.appendChild.callsFake(document.body.appendChild.bind(document.body));
8✔
178
      });
179

180
      it('sets up and runs renderer', () => {
1✔
181
        window._render = sinon.stub();
1✔
182
        const data = {
1✔
183
          message: MESSAGE_RESPONSE,
184
          adId: '123',
185
          renderer: 'window.render = window.parent._render'
186
        }
187
        renderAd({adId: '123', pubUrl: ORIGIN});
1✔
188
        reply(data);
1✔
189
        return waitFor(() => window._render.args.length).then(() => {
1✔
190
          sinon.assert.calledWith(window._render, data, sinon.match.any, win);
1✔
191
        }).finally(() => {
192
          delete window._render;
1✔
193
        })
194
      });
195

196
      Object.entries({
1✔
197
        'throws (w/error)': ['window.render = function() { throw new Error("msg") }'],
198
        'throws (w/reason)': ['window.render = function() { throw {reason: "other", message: "msg"}}', 'other'],
199
        'is missing': [null, ERROR_EXCEPTION, null],
200
        'rejects (w/error)': ['window.render = function() { return Promise.reject(new Error("msg")) }'],
201
        'rejects (w/reason)': ['window.render = function() { return Promise.reject({reason: "other", message: "msg"}) }', 'other'],
202
      }).forEach(([t, [renderer, reason = ERROR_EXCEPTION, message = 'msg']]) => {
5✔
203
        it(`signals AD_RENDER_FAILED on renderer that ${t}`, () => {
5✔
204
          renderAd({adId: '123', pubUrl: ORIGIN});
5✔
205
          reply({
5✔
206
            message: MESSAGE_RESPONSE,
207
            adId: '123',
208
            renderer
209
          });
210
          return waitFor(() => messages[1]?.payload).then(() => {
5✔
211
            sinon.assert.match(messages[1].payload, {
5✔
212
              adId: '123',
213
              message: MESSAGE_EVENT,
214
              event: EVENT_AD_RENDER_FAILED,
215
              info: {
216
                reason,
217
                message: sinon.match(val => message == null || message === val)
5✔
218
              }
219
            });
220
          })
221
        })
222
      });
223

224
      it('signals AD_RENDER_SUCCEEDED when renderer resolves', () => {
1✔
225
        renderAd({adId: '123', pubUrl: ORIGIN});
1✔
226
        reply({
1✔
227
          message: MESSAGE_RESPONSE,
228
          adId: '123',
229
          renderer: 'window.render = function() { return new Promise((resolve) => { window.parent._resolve = resolve })}'
230
        });
231
        return waitFor(() => window._resolve).then(() => {
1✔
232
          expect(messages[1]).to.not.exist;
1✔
233
          window._resolve();
1✔
234
          return waitFor(() => messages[1]?.payload)
1✔
235
        }).then(() => {
236
          sinon.assert.match(messages[1].payload, {
1✔
237
            adId: '123',
238
            message: MESSAGE_EVENT,
239
            event: EVENT_AD_RENDER_SUCCEEDED
240
          })
241
        }).finally(() => {
242
          delete window._resolve;
1✔
243
        })
244
      })
245

246
      it('is provided a sendMessage that accepts replies', () => {
1✔
247
        renderAd({adId: '123', pubUrl: ORIGIN});
1✔
248
        window._reply = sinon.stub();
1✔
249
        reply({
1✔
250
          adId: '123',
251
          message: MESSAGE_RESPONSE,
252
          renderer: 'window.render = function(_, {sendMessage}) { sendMessage("test", "data", function(reply) { window.parent._reply(reply) }) }'
253
        });
254
        return waitFor(() => messages[1]?.payload).then(() => {
1✔
255
          reply('response', 1);
1✔
256
          return waitFor(() => window._reply.args.length)
1✔
257
        }).then(() => {
258
          sinon.assert.calledWith(window._reply, sinon.match({data: JSON.stringify('response')}));
1✔
259
        }).finally(() => {
260
          delete window._reply;
1✔
261
        })
262
      });
263
    });
264
  });
265
});
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

© 2026 Coveralls, Inc