Code Review Videos > Broken Link Checker > TypeScript > Integrating Next Guessed Location

Integrating Next Guessed Location

If we take the getNextLocation function that we created previously, and put this into our implementation, does this solve the Microsoft redirect issue?

Well, rather than write a test for that, let’s just check it out by hacking it in to the current code. If it does work, we can think of a better way to solve the problem. And if it doesn’t, then we didn’t waste too much time trying it out.

  try {
    const result = await fetcher(href);

    const guessedNextLocation = getNextLocation(href, result.headers);
    console.log(`guessedNextLocation`, guessedNextLocation);

    if (guessedNextLocation) {
      const updatedRequests = [...requests, { ...result, redirected: true }];

      return await visit(guessedNextLocation, updatedRequests);
    }

    return [
      ...requests,
      {
        ...result,
        redirected: false,
      },
    ];
  } catch (e) {
Code language: TypeScript (typescript)

Which does seem to work:

I’m still not thrilled about needing to figure out the next location inside the visit function. I think I would prefer to move that inside the fetcher.

This should be fairly easy to do, as we have all the information in the fetcher already. Is it easy to update the tests though?

import { fetcher } from "./fetcher";

const fakeUrl = "http://some.fake.url";

describe("fetcher", () => {
  afterEach(() => {
    jest.resetAllMocks();
  });

  [
    {
      description: "empty headers",
      givenHeaders: new Headers(),
      expectedHeaders: {},
    },
    {
      description: "with headers",
      givenHeaders: new Headers({ some: "data" }),
      expectedHeaders: { some: "data" },
    },
  ].forEach(({ description, givenHeaders, expectedHeaders }) => {
    test(`should call the native fetch function in the expected manner: ${description}`, async () => {
      const fetchSpy = jest.spyOn(global, "fetch").mockResolvedValueOnce({
        url: fakeUrl,
        status: 200,
        statusText: "something",
        ok: true,
        headers: givenHeaders,
      } as unknown as Response);

      const result = await fetcher(fakeUrl);

      expect(fetchSpy).toHaveBeenCalledTimes(1);
      expect(fetchSpy).toHaveBeenCalledWith(fakeUrl, {
        redirect: "manual",
      });

      expect(result).toEqual({
        ok: true,
        status: 200,
        statusText: "something",
        url: "http://some.fake.url",
        headers: expectedHeaders,
      });
    });
  });
});Code language: TypeScript (typescript)

As best I can think, all we need to do here is to add the nextLocation key / value pair on to the expected result.

Those two tests don’t take a location header into account. I guess a new test is the best option:

  test("should guess the next location if applicable", async () => {
    const fakeLocationPath = "/a/b/c";

    jest.spyOn(global, "fetch").mockResolvedValueOnce({
      url: fakeUrl,
      status: 200,
      statusText: "something",
      ok: true,
      headers: new Headers({ location: fakeLocationPath }),
    } as unknown as Response);

    const result = await fetcher(fakeUrl);

    expect(result).toEqual({
      ok: true,
      status: 200,
      statusText: "something",
      url: "http://some.fake.url",
      headers: { location: fakeLocationPath },
      nextLocation: `${fakeUrl}${fakeLocationPath}`,
    });
  });
Code language: TypeScript (typescript)

And the implementation:

import { FetcherResponse } from "./types";
import { getNextLocation } from "../get-next-location";

export const fetcher = async (href: string): Promise<FetcherResponse> => {
  const { url, status, statusText, ok, headers } = await fetch(href, {
    redirect: "manual",
  });

  const headersObject = Object.fromEntries(headers);
  const nextLocation = getNextLocation(url, headersObject);

  return { url, status, statusText, ok, headers: headersObject, nextLocation };
};

Code language: TypeScript (typescript)

The nextLocation also needs adding to the FetcherResponse type:

export type FetcherResponse = {
  url: string;
  status: number;
  statusText: string;
  ok: boolean;
  headers: Record<string, string>;
  nextLocation: string | null;
};
Code language: TypeScript (typescript)

This type addition causes some chaos in the tests for visit and fetcher. I’d suggest checking out the code repo over on GitHub if following along and unable to fix for yourself.

No behaviour changes here, just a little internal tidy up.

There is still the outstanding issue of our function occasionally hanging when called. Let’s fix that next.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.