Skip to content

Conversation

@MillerSvt
Copy link
Contributor

@MillerSvt MillerSvt commented Jun 4, 2025

Fixed jsdom overriding problems from #15556

Guys, please share some examples with overriding JSDOM properties. I'll add test cases for it.

@netlify
Copy link

netlify bot commented Jun 4, 2025

Deploy Preview for jestjs ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit be05c87
🔍 Latest deploy log https://app.netlify.com/projects/jestjs/deploys/684090d4d913e5000888e5d4
😎 Deploy Preview https://deploy-preview-15652--jestjs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@MillerSvt MillerSvt force-pushed the add-allow-jsdom-mutations-options branch from be57ac9 to ac4012c Compare June 4, 2025 18:26
@MillerSvt MillerSvt force-pushed the add-allow-jsdom-mutations-options branch from ac4012c to be05c87 Compare June 4, 2025 18:30
@Stanzilla
Copy link

Stanzilla commented Jun 4, 2025

I have a bunch of window.location tests:

jest
  .spyOn(window, 'location', 'get')
  .mockImplementation(
    () => new URL('https://game.com') as unknown as Location
  );

or things like this

(global as any).location = new URL(
      'https://example.com/?use_social_login=true'
    );
const expectedRedirectUrl = async (
  client: AuthorizeResponse,
  response_type: 'token' | 'code',
  expectedUrl: string
) => {
  /**
   * Given there is an active session
   * And the client is already authorized
   */
  const store = setup({ client, response_type });
  /**
   * Then, when redirection occurs
   */
  store.dispatch(redirect());
  /**
   * I expect the redirect url to be a well-formed one
   */
  await waitFor(() => {
    expect(window.location.assign).toHaveBeenCalledWith(expectedUrl);
  });
};

const windowLocation = window.location;

describe('modules/redirect', () => {
  afterAll(() => {
    window.location = windowLocation;
  });

  beforeEach(() => {
    // @ts-ignore - Need to delete property first in test environment
    delete window.location;
    window.location = {
      ...windowLocation,
      assign: jest.fn()
    } as Location;
  });

  const redirect_uri_1 = 'https://client-site.com/';
  const redirect_uri_2 = 'https://client-site.com/?page=1';
  const expectedToken = '682a5-c9bb-4da6-9da7-7d93a9540306';
  const expectedCode = '123xyz';

  const testCases: RedirectType[] = [
    {
      description:
        'should redirect to a well-formed URL with token (no qs params)',
      client: mockAuthorizedClient({
        redirect_uri: redirect_uri_1,
        code: undefined,
        token: expectedToken
      }),
      response_type: 'token',
      expectedUrl: `${redirect_uri_1}#token=${expectedToken}`
    },
    {
      description:
        'should redirect to a well-formed URL with code (no qs params)',
      client: mockAuthorizedClient({
        redirect_uri: redirect_uri_1,
        code: expectedCode
      }),
      response_type: 'code',
      expectedUrl: `${redirect_uri_1}?code=${expectedCode}`
    },
    {
      description:
        'should redirect to a well-formed URL with token (existing qs params)',
      client: mockAuthorizedClient({
        redirect_uri: redirect_uri_2,
        code: undefined,
        token: expectedToken
      }),
      response_type: 'token',
      expectedUrl: `${redirect_uri_2}#token=${expectedToken}`
    },
    {
      description:
        'should redirect to a well-formed URL with code (existing qs params)',
      client: mockAuthorizedClient({
        redirect_uri: redirect_uri_2,
        code: expectedCode
      }),
      response_type: 'code',
      expectedUrl: `${redirect_uri_2}&code=${expectedCode}`
    }
  ];

  it.each(testCases)(
    'should redirect to a well-formed URL with $description',
    async ({ client, response_type, expectedUrl }) => {
      await expectedRedirectUrl(client, response_type, expectedUrl);
    }
  );

  it('should redirect to the exact `redirect_uri` in Zendesk login flow', async () => {
    const store = setup({ isZendeskLoginFlow: true });
    const zendeskRedirectUri = redirect_uri_2;

    /**
     * Given there is an active session
     * And no `client_id` or `response_type` in the qs params
     * Then, when redirection occurs
     */
    store.dispatch(redirect({ redirect_uri: zendeskRedirectUri }));

    /**
     * I expect the redirect url to match whatever is returned
     * in the `return_to` property in the `/api/v1/zendesk/token`
     * endpoint's response payload
     */
    await waitFor(() =>
      expect(window.location.assign).toHaveBeenCalledWith(zendeskRedirectUri)
    );
  });
});
/**
 * Checks if specified url has the same top domain as current
 * window hostname and hence can be considered safe for redirect.
 *
 * In case of a complete match for url origin `false` is return and
 * the url not considered a subdomain of the current window hostname.
 *
 * @param url - Url to check
 * @returns `true` if subdomain of current hostname top-level domain, `false`
 * otherwise
 */
export function isSubdomainRedirect(url: URL) {
  if (window.location.origin === url.origin) {
    return false;
  }
  const [topDomain, secondLevelDomain] = url.hostname.split('.').reverse();
  const [allowedTopDomain, allowedSecondLevelDomain] = window.location.hostname
    .split('.')
    .reverse();
  return (
    topDomain === allowedTopDomain &&
    secondLevelDomain === allowedSecondLevelDomain
  );
}

export default isSubdomainRedirect;

@cpojer
Copy link
Member

cpojer commented Jun 5, 2025

My main concern is that this is going to massively impact performance.

@MillerSvt
Copy link
Contributor Author

MillerSvt commented Jun 5, 2025

@cpojer yes, I haven't tested the performance yet, but I agree with you. So I made it a separate option. It's just a backup solution in case nothing better comes along.

@cpojer
Copy link
Member

cpojer commented Jun 5, 2025

I really appreciate the work on this, but I really don't think this is the way forward. I can see how somebody might provide their own version of a jsdom environment for Jest with this that they maintain and publish, but it should probably not be part of Jest Core. I'll write about how to work around the location.href situation.

@cpojer cpojer closed this Jun 5, 2025
@github-actions
Copy link

github-actions bot commented Jul 6, 2025

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 6, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants