How to mock window.top and window.self with Jest

If you're working with iframes to embed web content in other pages, your code might include checks like this:

if (window.top === window.self) {
// Code here would be executed when not in an iframe
console.log('We are not in an iframe!');
} else {
// Code here would be executed when in an iframe
console.log('We ARE in an iframe!');
}

What exactly is this code doing?

Detecting if you're inside an iframe

In a non-iframe situation, you have just one window object. You probably already know it as the topmost DOM element.

But when you're using iframes on a web page, you have a nested hierarchy of window objects instead; each iframe contains its own DOM, so there's one window at the top of each iframe.

When you have JavaScript running in any of these frames, you can reference window properties like top and self to tell where you are in the hierarchy:

  • window.self returns the current window itself
  • window.parent returns the immediate parent of the current window
  • window.top returns the topmost window in the hierarchy

Assuming we had a script running in the innermost frame, here's a visual example of that hierarchy:
Graphic demonstrating a hierarchy of window objects

So that's how window.top === window.self checks if the current context is an iframe, or the top level window.

Unit testing iframe checks with Jest

In Jest's test environment, your unit test code won't actually be running inside an iframe. So how can you test both branches of that kind of if/else statement?

Here's a simple example of the above check in a function:

function isInIframe() {
return window.top !== window.self;
}

It should return true if in an iframe, and false if not. I'll use it to demonstrate how you can mock the relevant properties in your tests.

Testing the positive (in iframe) case

Here's how we can test that this function returns true if executed in an iframe:

describe('isInIframe', () => {
it('should return true if in iframe', () => {
// Extract the real properties so we can put them back after this test
const { top, self } = window;

// Delete the real properties from window so we can mock them
delete window.top;
delete window.self;

// Mock window.top and window.self as different objects (we don't
// need anything else from the original objects here, so {} is fine)
window.top = {};
window.self = {};

// Test the code
const result = isInIframe();
expect(result).toBe(true);

// Restore the original values so other tests will function normally
window.top = top;
window.self = self;
});
});

Why does this work?

In JavaScript, objects are pass by reference. This means two empty objects can have the same value—they are both empty objects—but don't pass an equality check, because they have a different reference, or location in memory.

So we can use the fact that {} !== {} to force unit tests to execute the 'in iframe' branch.

In a simple test, it's enough to just use empty objects. But what if you need to access the other properties on either of the window objects?

You can make a copy of each of them (keeping the values but forcing each object to have a new reference) by destructuring:

window.top = {...window.top};
window.self = {...window.self};

Testing the negative (in top window) case

The non-iframe case is a lot simpler, because it's actually just the default behavior of the test environment:

describe('isInIframe', () => {
it('should return false if not in iframe', () => {
const result = isInIframe();
expect(result).toBe(false);
});
});

Succeed in tech

Get actionable tips on coding, getting hired and working as a developer.

I won't spam you. Unsubscribe any time.