'use strict';

describe('urlUtils', function() {
  describe('urlResolve', function() {
    it('should returned already parsed URLs unchanged', function() {
      var urlObj = urlResolve('/foo?bar=baz#qux');
      expect(urlResolve(urlObj)).toBe(urlObj);
      expect(urlResolve(true)).toBe(true);
      expect(urlResolve(null)).toBeNull();
      expect(urlResolve(undefined)).toBeUndefined();
    });


    it('should normalize a relative url', function() {
      expect(urlResolve('foo').href).toMatch(/^https?:\/\/[^/]+\/foo$/);
    });


    it('should parse relative URL into component pieces', function() {
      var parsed = urlResolve('foo');
      expect(parsed.href).toMatch(/https?:\/\//);
      expect(parsed.protocol).toMatch(/^https?/);
      expect(parsed.host).not.toBe('');
      expect(parsed.hostname).not.toBe('');
      expect(parsed.pathname).not.toBe('');
    });


    it('should return pathname as / if empty path provided', function() {
      // IE (all versions) counts / as empty, necessary to use / so that pathname is not context.html
      var parsed = urlResolve('/');
      expect(parsed.pathname).toBe('/');
    });

    it('should return an IPv6 hostname wrapped in brackets', function() {
      // Support: IE 9-11 only, Edge 16-17 only (fixed in 18 Preview)
      // IE/Edge don't wrap IPv6 addresses' hostnames in square brackets
      // when parsed out of an anchor element.
      var parsed = urlResolve('http://[::1]/');
      expect(parsed.hostname).toBe('[::1]');
    });

    it('should not put the domain in brackets for the hostname field', function() {
      var parsed = urlResolve('https://google.com/');
      expect(parsed.hostname).toBe('google.com');
    });
  });


  describe('urlIsSameOrigin and urlIsSameOriginAsBaseUrl', function() {
    it('should support various combinations of urls - both string and parsed',
      inject(function($document) {
        function expectIsSameOrigin(url, expectedValue) {
          expect(urlIsSameOrigin(url)).toBe(expectedValue);
          expect(urlIsSameOrigin(urlResolve(url))).toBe(expectedValue);

          // urlIsSameOriginAsBaseUrl() should behave the same as urlIsSameOrigin() by default.
          // Behavior when there is a non-default base URL or when the base URL changes dynamically
          // is tested in the end-to-end tests in e2e/tests/base-tag.spec.js.
          expect(urlIsSameOriginAsBaseUrl(url)).toBe(expectedValue);
          expect(urlIsSameOriginAsBaseUrl(urlResolve(url))).toBe(expectedValue);
        }

        expectIsSameOrigin('path', true);

        var origin = urlResolve($document[0].location.href);
        expectIsSameOrigin('//' + origin.host + '/path', true);

        // Different domain.
        expectIsSameOrigin('http://example.com/path', false);

        // Auto fill protocol.
        expectIsSameOrigin('//example.com/path', false);

        // Should not match when the ports are different.
        // This assumes that the test is *not* running on port 22 (very unlikely).
        expectIsSameOrigin('//' + origin.hostname + ':22/path', false);
      })
    );
  });


  describe('urlIsAllowedOriginFactory', function() {
    var origin = urlResolve(window.location.href);
    var urlIsAllowedOrigin;

    beforeEach(function() {
      urlIsAllowedOrigin = urlIsAllowedOriginFactory([
        'https://foo.com/',
        origin.protocol + '://bar.com:1337/'
      ]);
    });


    it('should implicitly allow the current origin', function() {
      expect(urlIsAllowedOrigin('path')).toBe(true);
    });


    it('should check against the list of whitelisted origins', function() {
      expect(urlIsAllowedOrigin('https://foo.com/path')).toBe(true);
      expect(urlIsAllowedOrigin(origin.protocol + '://bar.com:1337/path')).toBe(true);
      expect(urlIsAllowedOrigin('https://baz.com:1337/path')).toBe(false);
      expect(urlIsAllowedOrigin('https://qux.com/path')).toBe(false);
    });


    it('should support both strings and parsed URL objects', function() {
      expect(urlIsAllowedOrigin('path')).toBe(true);
      expect(urlIsAllowedOrigin(urlResolve('path'))).toBe(true);
      expect(urlIsAllowedOrigin('https://foo.com/path')).toBe(true);
      expect(urlIsAllowedOrigin(urlResolve('https://foo.com/path'))).toBe(true);
    });


    it('should return true only if the origins (protocol, hostname, post) match', function() {
      var differentProtocol = (origin.protocol !== 'http') ? 'http' : 'https';
      var differentPort = (parseInt(origin.port, 10) || 0) + 1;
      var url;


      // Relative path
      url = 'path';
      expect(urlIsAllowedOrigin(url)).toBe(true);


      // Same origin
      url = origin.protocol + '://' + origin.host + '/path';
      expect(urlIsAllowedOrigin(url)).toBe(true);

      // Same origin - implicit protocol
      url = '//' + origin.host + '/path';
      expect(urlIsAllowedOrigin(url)).toBe(true);

      // Same origin - different protocol
      url = differentProtocol + '://' + origin.host + '/path';
      expect(urlIsAllowedOrigin(url)).toBe(false);

      // Same origin - different port
      url = origin.protocol + '://' + origin.hostname + ':' + differentPort + '/path';
      expect(urlIsAllowedOrigin(url)).toBe(false);


      // Allowed origin
      url = origin.protocol + '://bar.com:1337/path';
      expect(urlIsAllowedOrigin(url)).toBe(true);

      // Allowed origin - implicit protocol
      url = '//bar.com:1337/path';
      expect(urlIsAllowedOrigin(url)).toBe(true);

      // Allowed origin - different protocol
      url = differentProtocol + '://bar.com:1337/path';
      expect(urlIsAllowedOrigin(url)).toBe(false);

      // Allowed origin - different port
      url = origin.protocol + '://bar.com:1338/path';
      expect(urlIsAllowedOrigin(url)).toBe(false);
    });
  });
});
