1

Why does the following code produce a 404 status code for http://localhost:4200/page yet a 200 status code for http://localhost:4200?

When Protractor is running I'm able to open up another browser window and type http://localhost:4200/page into the address bar, press enter, and it works. But request in Protractor gives a 404.

import * as Request from 'request';
describe('Link', () => {
  it('should work', async () => {
    const href = 'http://localhost:4200'; // works
    // const href = 'http://localhost:4200/page'; // doesn't work, even though this works outside of Protractor while Protractor is running
    const statusCode = await new Promise<number>((resolve, reject) => {
      Request(href, (error, response, body) => {
        if (error) {
          reject(error);
        } else {
          resolve(response.statusCode);
        }
      });
    });
    if (typeof(statusCode) !== 'number') {
      throw new Error(`Failed to request ${href}`);
    }
    if (statusCode < 200 || statusCode >= 300) {
      throw new Error(`Bad status code ${statusCode} for ${href}`);
    }
  });
});

Here's a complete, minimal, verifiable repro: https://drive.google.com/uc?export=download&id=1S2It1jA1bTR1hUoqdd_QC_Qa6BB3wnDS

  1. Run ng serve and navigate to http://localhost:4200/page directly in your browser of choice to observe the page does exist
  2. Run npm install then ng e2e to observe failure on http://localhost:4200/page URL
  3. Modify the E2E test to delay for a long time so that you can do this:
    1. ng e2e
    2. While Protractor is still running your long delayed test, navigate to http://localhost:4200/page directly in your browser of choice to observe the page does exist while Protractor is running

Note that Git history is included in the zip.

Matt Thomas
  • 5,279
  • 4
  • 27
  • 59
  • I downloaded your project and can see that http:localhost:4200/{any random string} will retrieve pages component.html indicating that `pages` is not setup as an endpoint itself. You have some sort of catch in your routing most likely. My Angular knowledge is strong enough right now to provide more sight than that however – DublinDev Jun 20 '19 at 18:08
  • @DublinDev Sorry for the confusion. I used `**` in the routing module in this example to eliminate any suspicion about spelling issues. The route's path can be changed from `**` to `pages` and the issue still arises. Also you'll notice that `ng serve` then navigate to `http://localhost:4200/pages` works with the code as-is. More info on the `**` here: https://angular.io/api/router/Route#path – Matt Thomas Jun 20 '19 at 18:16
  • 1
    Just verified that and you are correct. However direct requests through postman to /pages also fail so this is not an issue with protractor or request. I feel like this is something to do with how angular reaches components in a rest manner without actually establishing them as endpoints but, as I said, I could be way wrong on this. – DublinDev Jun 20 '19 at 18:52
  • 1
    @DublinDev Your find led me to my answer below. Turns out the routing was fine, but the Angular dev server returns 404s without a proper `Accept` HTTP header. Much thanks! – Matt Thomas Jun 21 '19 at 13:33
  • Glad to hear it, congrats on getting it resolved! – DublinDev Jun 21 '19 at 13:36

1 Answers1

0

The 404 is from a missing Accept: text/html header. Add that header and you get 200.

As DublinDev pointed out it doesn't work in Postman, either, so it's not specifically a request issue. But it does work in Chrome. That smells like a headers issue. Sure enough, after messing around with headers for a little bit I narrowed it down to this one.

Here's the final product:

const statusCode = await new Promise<number>((resolve, reject) => {
  Request({
    url: href,
    headers: {
      'Accept': 'text/html'
    }
  }, (error, response, body) => {
    if (error) {
      reject(error);
    } else {
      resolve(response.statusCode);
    }
  });
});

So in summary: that header is necessary because the Angular site is hosted by Angular's dev server during E2E tests, and the dev server apparently has this quirk.

Matt Thomas
  • 5,279
  • 4
  • 27
  • 59