<commit_before>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import * as trackPromise from '../../src/impression';
import {Observable} from '../../src/observable';
import {Services} from '../../src/services';
import {cidServiceForDocForTesting} from
  '../../src/service/cid-impl';
import {createIframePromise} from '../../testing/iframe';
import {
  extractClientIdFromGaCookie,
  installUrlReplacementsServiceForDoc,
} from '../../src/service/url-replacements-impl';
import {getMode} from '../../src/mode';
import {
  installActivityServiceForTesting,
} from '../../extensions/amp-analytics/0.1/activity-impl';
import {installCryptoService} from '../../src/service/crypto-impl';
import {installDocService} from '../../src/service/ampdoc-impl';
import {installDocumentInfoServiceForDoc} from
  '../../src/service/document-info-impl';
import {
  markElementScheduledForTesting,
  resetScheduledElementForTesting,
} from '../../src/service/custom-element-registry';
import {
  mockWindowInterface,
  stubServiceForDoc,
} from '../../testing/test-helper';
import {parseUrlDeprecated} from '../../src/url';
import {registerServiceBuilder} from '../../src/service';
import {setCookie} from '../../src/cookies';
import {user} from '../../src/log';


describes.sandboxed('UrlReplacements', {}, () => {

  let canonical;
  let loadObservable;
  let replacements;
  let viewerService;
  let userErrorStub;
  let ampdoc;

  beforeEach(() => {
    canonical = 'https://canonical.com/doc1';
    userErrorStub = sandbox.stub(user(), 'error');
  });

  function getReplacements(opt_options) {
    return createIframePromise().then(iframe => {
      ampdoc = iframe.ampdoc;
      iframe.doc.title = 'Pixel Test';
      const link = iframe.doc.createElement('link');
      link.setAttribute('href', 'https://pinterest.com:8080/pin1');
      link.setAttribute('rel', 'canonical');
      iframe.doc.head.appendChild(link);
      installDocumentInfoServiceForDoc(iframe.ampdoc);
      resetScheduledElementForTesting(iframe.win, 'amp-analytics');
      resetScheduledElementForTesting(iframe.win, 'amp-experiment');
      resetScheduledElementForTesting(iframe.win, 'amp-share-tracking');
      if (opt_options) {
        if (opt_options.withCid) {
          markElementScheduledForTesting(iframe.win, 'amp-analytics');
          cidServiceForDocForTesting(iframe.ampdoc);
          installCryptoService(iframe.win);
        }
        if (opt_options.withActivity) {
          markElementScheduledForTesting(iframe.win, 'amp-analytics');
          installActivityServiceForTesting(iframe.ampdoc);
        }
        if (opt_options.withVariant) {
          markElementScheduledForTesting(iframe.win, 'amp-experiment');
          registerServiceBuilder(iframe.win, 'variant', function() {
            return Promise.resolve({
              'x1': 'v1',
              'x2': null,
            });
          });
        }
        if (opt_options.withShareTracking) {
          markElementScheduledForTesting(iframe.win, 'amp-share-tracking');
          registerServiceBuilder(iframe.win, 'share-tracking', function() {
            return Promise.resolve({
              incomingFragment: '12345',
              outgoingFragment: '54321',
            });
          });
        }
        if (opt_options.withStoryVariableService) {
          markElementScheduledForTesting(iframe.win, 'amp-story');
          registerServiceBuilder(iframe.win, 'story-variable', function() {
            return Promise.resolve({
              pageIndex: 546,
              pageId: 'id-123',
            });
          });
        }
        if (opt_options.withViewerIntegrationVariableService) {
          markElementScheduledForTesting(iframe.win, 'amp-viewer-integration');
          registerServiceBuilder(iframe.win, 'viewer-integration-variable',
              function() {
                return Promise.resolve(
                    opt_options.withViewerIntegrationVariableService);
              });
        }
        if (opt_options.withOriginalTitle) {
          iframe.doc.originalTitle = 'Original Pixel Test';
        }
      }
      viewerService = Services.viewerForDoc(iframe.ampdoc);
      replacements = Services.urlReplacementsForDoc(iframe.ampdoc);
      return replacements;
    });
  }

  function expandUrlAsync(url, opt_bindings, opt_options) {
    return getReplacements(opt_options).then(
        replacements => replacements.expandUrlAsync(url, opt_bindings)
    );
  }

  function getFakeWindow() {
    loadObservable = new Observable();
    const win = {
      addEventListener(type, callback) {
        loadObservable.add(callback);
      },
      Object,
      performance: {
        timing: {
          navigationStart: 100,
          loadEventStart: 0,
        },
      },
      removeEventListener(type, callback) {
        loadObservable.remove(callback);
      },
      document: {
        nodeType: /* document */ 9,
        querySelector: selector => {
          if (selector.startsWith('meta')) {
            return {
              getAttribute: () => {return 'https://whitelisted.com https://greylisted.com http://example.com';},
              hasAttribute: () => {return true;},
            };
          } else {
            return {href: canonical};
          }
        },
        getElementById: () => {},
        cookie: '',
      },
      Math: {
        random: () => 0.1234,
      },
      services: {
        'viewport': {obj: {}},
        'cid': {
          promise: Promise.resolve({
            get: config => Promise.resolve('test-cid(' + config.scope + ')'),
          }),
        },
      },
    };
    win.document.defaultView = win;
    installDocService(win, /* isSingleDoc */ true);
    const ampdoc = Services.ampdocServiceFor(win).getAmpDoc();
    installDocumentInfoServiceForDoc(ampdoc);
    win.ampdoc = ampdoc;
    installUrlReplacementsServiceForDoc(ampdoc);
    return win;
  }

  it('should replace RANDOM', () => {
    return expandUrlAsync('ord=RANDOM?').then(res => {
      expect(res).to.match(/ord=(\d+(\.\d+)?)\?$/);
    });
  });

  it('should replace COUNTER', () => {
    return expandUrlAsync(
        'COUNTER(foo),COUNTER(bar),COUNTER(foo),COUNTER(bar),COUNTER(bar)')<commit_msg>Restrict the number of global replacements variable (#19233)<commit_after>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import * as trackPromise from '../../src/impression';
import {Observable} from '../../src/observable';
import {Services} from '../../src/services';
import {cidServiceForDocForTesting} from
  '../../src/service/cid-impl';
import {createIframePromise} from '../../testing/iframe';
import {
  extractClientIdFromGaCookie,
  installUrlReplacementsServiceForDoc,
} from '../../src/service/url-replacements-impl';
import {getMode} from '../../src/mode';
import {
  installActivityServiceForTesting,
} from '../../extensions/amp-analytics/0.1/activity-impl';
import {installCryptoService} from '../../src/service/crypto-impl';
import {installDocService} from '../../src/service/ampdoc-impl';
import {installDocumentInfoServiceForDoc} from
  '../../src/service/document-info-impl';
import {
  markElementScheduledForTesting,
  resetScheduledElementForTesting,
} from '../../src/service/custom-element-registry';
import {
  mockWindowInterface,
  stubServiceForDoc,
} from '../../testing/test-helper';
import {parseUrlDeprecated} from '../../src/url';
import {registerServiceBuilder} from '../../src/service';
import {setCookie} from '../../src/cookies';
import {user} from '../../src/log';


describes.sandboxed('UrlReplacements', {}, () => {

  let canonical;
  let loadObservable;
  let replacements;
  let viewerService;
  let userErrorStub;
  let ampdoc;

  beforeEach(() => {
    canonical = 'https://canonical.com/doc1';
    userErrorStub = sandbox.stub(user(), 'error');
  });

  function getReplacements(opt_options) {
    return createIframePromise().then(iframe => {
      ampdoc = iframe.ampdoc;
      iframe.doc.title = 'Pixel Test';
      const link = iframe.doc.createElement('link');
      link.setAttribute('href', 'https://pinterest.com:8080/pin1');
      link.setAttribute('rel', 'canonical');
      iframe.doc.head.appendChild(link);
      installDocumentInfoServiceForDoc(iframe.ampdoc);
      resetScheduledElementForTesting(iframe.win, 'amp-analytics');
      resetScheduledElementForTesting(iframe.win, 'amp-experiment');
      resetScheduledElementForTesting(iframe.win, 'amp-share-tracking');
      if (opt_options) {
        if (opt_options.withCid) {
          markElementScheduledForTesting(iframe.win, 'amp-analytics');
          cidServiceForDocForTesting(iframe.ampdoc);
          installCryptoService(iframe.win);
        }
        if (opt_options.withActivity) {
          markElementScheduledForTesting(iframe.win, 'amp-analytics');
          installActivityServiceForTesting(iframe.ampdoc);
        }
        if (opt_options.withVariant) {
          markElementScheduledForTesting(iframe.win, 'amp-experiment');
          registerServiceBuilder(iframe.win, 'variant', function() {
            return Promise.resolve({
              'x1': 'v1',
              'x2': null,
            });
          });
        }
        if (opt_options.withShareTracking) {
          markElementScheduledForTesting(iframe.win, 'amp-share-tracking');
          registerServiceBuilder(iframe.win, 'share-tracking', function() {
            return Promise.resolve({
              incomingFragment: '12345',
              outgoingFragment: '54321',
            });
          });
        }
        if (opt_options.withStoryVariableService) {
          markElementScheduledForTesting(iframe.win, 'amp-story');
          registerServiceBuilder(iframe.win, 'story-variable', function() {
            return Promise.resolve({
              pageIndex: 546,
              pageId: 'id-123',
            });
          });
        }
        if (opt_options.withViewerIntegrationVariableService) {
          markElementScheduledForTesting(iframe.win, 'amp-viewer-integration');
          registerServiceBuilder(iframe.win, 'viewer-integration-variable',
              function() {
                return Promise.resolve(
                    opt_options.withViewerIntegrationVariableService);
              });
        }
        if (opt_options.withOriginalTitle) {
          iframe.doc.originalTitle = 'Original Pixel Test';
        }
      }
      viewerService = Services.viewerForDoc(iframe.ampdoc);
      replacements = Services.urlReplacementsForDoc(iframe.ampdoc);
      return replacements;
    });
  }

  function expandUrlAsync(url, opt_bindings, opt_options) {
    return getReplacements(opt_options).then(
        replacements => replacements.expandUrlAsync(url, opt_bindings)
    );
  }

  function getFakeWindow() {
    loadObservable = new Observable();
    const win = {
      addEventListener(type, callback) {
        loadObservable.add(callback);
      },
      Object,
      performance: {
        timing: {
          navigationStart: 100,
          loadEventStart: 0,
        },
      },
      removeEventListener(type, callback) {
        loadObservable.remove(callback);
      },
      document: {
        nodeType: /* document */ 9,
        querySelector: selector => {
          if (selector.startsWith('meta')) {
            return {
              getAttribute: () => {return 'https://whitelisted.com https://greylisted.com http://example.com';},
              hasAttribute: () => {return true;},
            };
          } else {
            return {href: canonical};
          }
        },
        getElementById: () => {},
        cookie: '',
      },
      Math: {
        random: () => 0.1234,
      },
      services: {
        'viewport': {obj: {}},
        'cid': {
          promise: Promise.resolve({
            get: config => Promise.resolve('test-cid(' + config.scope + ')'),
          }),
        },
      },
    };
    win.document.defaultView = win;
    installDocService(win, /* isSingleDoc */ true);
    const ampdoc = Services.ampdocServiceFor(win).getAmpDoc();
    installDocumentInfoServiceForDoc(ampdoc);
    win.ampdoc = ampdoc;
    installUrlReplacementsServiceForDoc(ampdoc);
    return win;
  }

  it('limit replacement params size', () => {
    return getReplacements().then(replacements => {
      replacements.getVariableSource().initialize();
      const variables =
          Object.keys(replacements.getVariableSource().replacements_);
      // Restrict the number of replacement params to globalVaraibleSource
      // Please consider adding the logic to amp-analytics instead.
      // Please contact @lannka or @zhouyx if the test fail.
      expect(variables.length).to.equal(69);
    });
  });

  it('should replace RANDOM', () => {
    return expandUrlAsync('ord=RANDOM?').then(res => {
      expect(res).to.match(/ord=(\d+(\.\d+)?)\?$/);
    });
  });

  it('should replace COUNTER', () => {
    return expandUrlAsync(
        'COUNTER(foo),COUNTER(bar),COUNTER(foo),COUNTER(bar),COUNTER(bar)')
{'fraction_non_alphanumeric': 0.13681011942618895, 'fraction_numerical': 0.006854639248109674, 'mean_word_length': 2.653071760454311, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 31, 'https://': 8, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7545243', 'n_tokens_mistral': 4062, 'n_tokens_neox': 3919, 'n_words': 776}