moduleFor(
  'The {{link-to}} helper: invoking with query params',
  class extends ApplicationTestCase {
    constructor() {
      super();
      let indexProperties = {
        foo: '123',
        bar: 'abc',
      };
      this.add(
        'controller:index',
        Controller.extend({
          queryParams: ['foo', 'bar', 'abool'],
          foo: indexProperties.foo,
          bar: indexProperties.bar,
          boundThing: 'OMG',
          abool: true,
        })
      );
      this.add(
        'controller:about',
        Controller.extend({
          queryParams: ['baz', 'bat'],
          baz: 'alex',
          bat: 'borf',
        })
      );
      this.indexProperties = indexProperties;
    }

    shouldNotBeActive(assert, selector) {
      this.checkActive(assert, selector, false);
    }

    shouldBeActive(assert, selector) {
      this.checkActive(assert, selector, true);
    }

    getController(name) {
      return this.applicationInstance.lookup(`controller:${name}`);
    }

    checkActive(assert, selector, active) {
      let classList = this.$(selector)[0].className;
      assert.equal(
        classList.indexOf('active') > -1,
        active,
        selector + ' active should be ' + active.toString()
      );
    }

    [`@test doesn't update controller QP properties on current route when invoked`](assert) {
      this.addTemplate(
        'index',
        `
      {{#link-to 'index' id='the-link'}}Index{{/link-to}}
    `
      );

      return this.visit('/').then(() => {
        this.click('#the-link');
        let indexController = this.getController('index');

        assert.deepEqual(
          indexController.getProperties('foo', 'bar'),
          this.indexProperties,
          'controller QP properties do not update'
        );
      });
    }

    [`@test doesn't update controller QP properties on current route when invoked (empty query-params obj)`](
      assert
    ) {
      this.addTemplate(
        'index',
        `
      {{#link-to 'index' (query-params) id='the-link'}}Index{{/link-to}}
    `
      );

      return this.visit('/').then(() => {
        this.click('#the-link');
        let indexController = this.getController('index');

        assert.deepEqual(
          indexController.getProperties('foo', 'bar'),
          this.indexProperties,
          'controller QP properties do not update'
        );
      });
    }

    [`@test doesn't update controller QP properties on current route when invoked (empty query-params obj, inferred route)`](
      assert
    ) {
      this.addTemplate(
        'index',
        `
      {{#link-to (query-params) id='the-link'}}Index{{/link-to}}
    `
      );

      return this.visit('/').then(() => {
        this.click('#the-link');
        let indexController = this.getController('index');

        assert.deepEqual(
          indexController.getProperties('foo', 'bar'),
          this.indexProperties,
          'controller QP properties do not update'
        );
      });
    }

    ['@test updates controller QP properties on current route when invoked'](assert) {
      this.addTemplate(
        'index',
        `
      {{#link-to 'index' (query-params foo='456') id="the-link"}}
        Index
      {{/link-to}}
    `
      );

      return this.visit('/').then(() => {
        this.click('#the-link');
        let indexController = this.getController('index');

        assert.deepEqual(
          indexController.getProperties('foo', 'bar'),
          { foo: '456', bar: 'abc' },
          'controller QP properties updated'
        );
      });
    }

    ['@test updates controller QP properties on current route when invoked (inferred route)'](
      assert
    ) {
      this.addTemplate(
        'index',
        `
      {{#link-to (query-params foo='456') id="the-link"}}
        Index
      {{/link-to}}
    `
      );

      return this.visit('/').then(() => {
        this.click('#the-link');
        let indexController = this.getController('index');

        assert.deepEqual(
          indexController.getProperties('foo', 'bar'),
          { foo: '456', bar: 'abc' },
          'controller QP properties updated'
        );
      });
    }

    ['@test updates controller QP properties on other route after transitioning to that route'](
      assert
    ) {
      this.router.map(function() {
        this.route('about');
      });

      this.addTemplate(
        'index',
        `
      {{#link-to 'about' (query-params baz='lol') id='the-link'}}
        About
      {{/link-to}}
    `
      );

      return this.visit('/').then(() => {
        let theLink = this.$('#the-link');
        assert.equal(theLink.attr('href'), '/about?baz=lol');

        this.runTask(() => this.click('#the-link'));

        let aboutController = this.getController('about');

        assert.deepEqual(
          aboutController.getProperties('baz', 'bat'),
          { baz: 'lol', bat: 'borf' },
          'about controller QP properties updated'
        );
      });
    }

    ['@test supplied QP properties can be bound'](assert) {
      this.addTemplate(
        'index',
        `
      {{#link-to (query-params foo=boundThing) id='the-link'}}Index{{/link-to}}
    `
      );

      return this.visit('/').then(() => {
        let indexController = this.getController('index');
        let theLink = this.$('#the-link');

        assert.equal(theLink.attr('href'), '/?foo=OMG');

        this.runTask(() => indexController.set('boundThing', 'ASL'));

        assert.equal(theLink.attr('href'), '/?foo=ASL');
      });
    }

    ['@test supplied QP properties can be bound (booleans)'](assert) {
      this.addTemplate(
        'index',
        `
      {{#link-to (query-params abool=boundThing) id='the-link'}}
        Index
      {{/link-to}}
    `
      );

      return this.visit('/').then(() => {
        let indexController = this.getController('index');
        let theLink = this.$('#the-link');

        assert.equal(theLink.attr('href'), '/?abool=OMG');

        this.runTask(() => indexController.set('boundThing', false));

        assert.equal(theLink.attr('href'), '/?abool=false');

        this.click('#the-link');

        assert.deepEqual(
          indexController.getProperties('foo', 'bar', 'abool'),
          { foo: '123', bar: 'abc', abool: false },
          'bound bool QP properties update'
        );
      });
    }
    ['@test href updates when unsupplied controller QP props change'](assert) {
      this.addTemplate(
        'index',
        `
      {{#link-to (query-params foo='lol') id='the-link'}}Index{{/link-to}}
    `
      );

      return this.visit('/').then(() => {
        let indexController = this.getController('index');
        let theLink = this.$('#the-link');

        assert.equal(theLink.attr('href'), '/?foo=lol');

        this.runTask(() => indexController.set('bar', 'BORF'));

        assert.equal(theLink.attr('href'), '/?bar=BORF&foo=lol');

        this.runTask(() => indexController.set('foo', 'YEAH'));

        assert.equal(theLink.attr('href'), '/?bar=BORF&foo=lol');
      });
    }

    ['@test The {{link-to}} with only query params always transitions to the current route with the query params applied'](
      assert
    ) {
      // Test harness for bug #12033
      this.addTemplate(
        'cars',
        `
      {{#link-to 'cars.create' id='create-link'}}Create new car{{/link-to}}
      {{#link-to (query-params page='2') id='page2-link'}}Page 2{{/link-to}}
      {{outlet}}
    `
      );
      this.addTemplate(
        'cars.create',
        `{{#link-to 'cars' id='close-link'}}Close create form{{/link-to}}`
      );

      this.router.map(function() {
        this.route('cars', function() {
          this.route('create');
        });
      });

      this.add(
        'controller:cars',
        Controller.extend({
          queryParams: ['page'],
          page: 1,
        })
      );

      return this.visit('/cars/create').then(() => {
        let router = this.appRouter;
        let carsController = this.getController('cars');

        assert.equal(router.currentRouteName, 'cars.create');

        this.runTask(() => this.click('#close-link'));

        assert.equal(router.currentRouteName, 'cars.index');
        assert.equal(router.get('url'), '/cars');
        assert.equal(carsController.get('page'), 1, 'The page query-param is 1');

        this.runTask(() => this.click('#page2-link'));

        assert.equal(router.currentRouteName, 'cars.index', 'The active route is still cars');
        assert.equal(router.get('url'), '/cars?page=2', 'The url has been updated');
        assert.equal(carsController.get('page'), 2, 'The query params have been updated');
      });
    }

    ['@test the {{link-to}} applies activeClass when query params are not changed'](assert) {
      this.addTemplate(
        'index',
        `
      {{#link-to (query-params foo='cat') id='cat-link'}}Index{{/link-to}}
      {{#link-to (query-params foo='dog') id='dog-link'}}Index{{/link-to}}
      {{#link-to 'index' id='change-nothing'}}Index{{/link-to}}
    `
      );
      this.addTemplate(
        'search',
        `
      {{#link-to (query-params search='same') id='same-search'}}Index{{/link-to}}
      {{#link-to (query-params search='change') id='change-search'}}Index{{/link-to}}
      {{#link-to (query-params search='same' archive=true) id='same-search-add-archive'}}Index{{/link-to}}
      {{#link-to (query-params archive=true) id='only-add-archive'}}Index{{/link-to}}
      {{#link-to (query-params search='same' archive=true) id='both-same'}}Index{{/link-to}}
      {{#link-to (query-params search='different' archive=true) id='change-one'}}Index{{/link-to}}
      {{#link-to (query-params search='different' archive=false) id='remove-one'}}Index{{/link-to}}
      {{outlet}}
    `
      );
      this.addTemplate(
        'search.results',
        `
      {{#link-to (query-params sort='title') id='same-sort-child-only'}}Index{{/link-to}}
      {{#link-to (query-params search='same') id='same-search-parent-only'}}Index{{/link-to}}
      {{#link-to (query-params search='change') id='change-search-parent-only'}}Index{{/link-to}}
      {{#link-to (query-params search='same' sort='title') id='same-search-same-sort-child-and-parent'}}Index{{/link-to}}
      {{#link-to (query-params search='same' sort='author') id='same-search-different-sort-child-and-parent'}}Index{{/link-to}}
      {{#link-to (query-params search='change' sort='title') id='change-search-same-sort-child-and-parent'}}Index{{/link-to}}
      {{#link-to (query-params foo='dog') id='dog-link'}}Index{{/link-to}}
    `
      );

      this.router.map(function() {
        this.route('search', function() {
          this.route('results');
        });
      });

      this.add(
        'controller:search',
        Controller.extend({
          queryParams: ['search', 'archive'],
          search: '',
          archive: false,
        })
      );

      this.add(
        'controller:search.results',
        Controller.extend({
          queryParams: ['sort', 'showDetails'],
          sort: 'title',
          showDetails: true,
        })
      );

      return this.visit('/')
        .then(() => {
          this.shouldNotBeActive(assert, '#cat-link');
          this.shouldNotBeActive(assert, '#dog-link');

          return this.visit('/?foo=cat');
        })
        .then(() => {
          this.shouldBeActive(assert, '#cat-link');
          this.shouldNotBeActive(assert, '#dog-link');

          return this.visit('/?foo=dog');
        })
        .then(() => {
          this.shouldBeActive(assert, '#dog-link');
          this.shouldNotBeActive(assert, '#cat-link');
          this.shouldBeActive(assert, '#change-nothing');

          return this.visit('/search?search=same');
        })
        .then(() => {
          this.shouldBeActive(assert, '#same-search');
          this.shouldNotBeActive(assert, '#change-search');
          this.shouldNotBeActive(assert, '#same-search-add-archive');
          this.shouldNotBeActive(assert, '#only-add-archive');
          this.shouldNotBeActive(assert, '#remove-one');

          return this.visit('/search?search=same&archive=true');
        })
        .then(() => {
          this.shouldBeActive(assert, '#both-same');
          this.shouldNotBeActive(assert, '#change-one');

          return this.visit('/search/results?search=same&sort=title&showDetails=true');
        })
        .then(() => {
          this.shouldBeActive(assert, '#same-sort-child-only');
          this.shouldBeActive(assert, '#same-search-parent-only');
          this.shouldNotBeActive(assert, '#change-search-parent-only');
          this.shouldBeActive(assert, '#same-search-same-sort-child-and-parent');
          this.shouldNotBeActive(assert, '#same-search-different-sort-child-and-parent');
          this.shouldNotBeActive(assert, '#change-search-same-sort-child-and-parent');
        });
    }

    ['@test the {{link-to}} applies active class when query-param is a number'](assert) {
      this.addTemplate(
        'index',
        `
      {{#link-to (query-params page=pageNumber) id='page-link'}}
        Index
      {{/link-to}}
    `
      );
      this.add(
        'controller:index',
        Controller.extend({
          queryParams: ['page'],
          page: 1,
          pageNumber: 5,
        })
      );

      return this.visit('/')
        .then(() => {
          this.shouldNotBeActive(assert, '#page-link');
          return this.visit('/?page=5');
        })
        .then(() => {
          this.shouldBeActive(assert, '#page-link');
        });
    }

    ['@test the {{link-to}} applies active class when query-param is an array'](assert) {
      this.addTemplate(
        'index',
        `
      {{#link-to (query-params pages=pagesArray) id='array-link'}}Index{{/link-to}}
      {{#link-to (query-params pages=biggerArray) id='bigger-link'}}Index{{/link-to}}
      {{#link-to (query-params pages=emptyArray) id='empty-link'}}Index{{/link-to}}
    `
      );

      this.add(
        'controller:index',
        Controller.extend({
          queryParams: ['pages'],
          pages: [],
          pagesArray: [1, 2],
          biggerArray: [1, 2, 3],
          emptyArray: [],
        })
      );

      return this.visit('/')
        .then(() => {
          this.shouldNotBeActive(assert, '#array-link');

          return this.visit('/?pages=%5B1%2C2%5D');
        })
        .then(() => {
          this.shouldBeActive(assert, '#array-link');
          this.shouldNotBeActive(assert, '#bigger-link');
          this.shouldNotBeActive(assert, '#empty-link');

          return this.visit('/?pages=%5B2%2C1%5D');
        })
        .then(() => {
          this.shouldNotBeActive(assert, '#array-link');
          this.shouldNotBeActive(assert, '#bigger-link');
          this.shouldNotBeActive(assert, '#empty-link');

          return this.visit('/?pages=%5B1%2C2%2C3%5D');
        })
        .then(() => {
          this.shouldBeActive(assert, '#bigger-link');
          this.shouldNotBeActive(assert, '#array-link');
          this.shouldNotBeActive(assert, '#empty-link');
        });
    }
    ['@test the {{link-to}} helper applies active class to the parent route'](assert) {
      this.router.map(function() {
        this.route('parent', function() {
          this.route('child');
        });
      });

      this.addTemplate(
        'application',
        `
      {{#link-to 'parent' id='parent-link'}}Parent{{/link-to}}
      {{#link-to 'parent.child' id='parent-child-link'}}Child{{/link-to}}
      {{#link-to 'parent' (query-params foo=cat) id='parent-link-qp'}}Parent{{/link-to}}
      {{outlet}}
    `
      );

      this.add(
        'controller:parent.child',
        Controller.extend({
          queryParams: ['foo'],
          foo: 'bar',
        })
      );

      return this.visit('/')
        .then(() => {
          this.shouldNotBeActive(assert, '#parent-link');
          this.shouldNotBeActive(assert, '#parent-child-link');
          this.shouldNotBeActive(assert, '#parent-link-qp');
          return this.visit('/parent/child?foo=dog');
        })
        .then(() => {
          this.shouldBeActive(assert, '#parent-link');
          this.shouldNotBeActive(assert, '#parent-link-qp');
        });
    }

    ['@test The {{link-to}} helper disregards query-params in activeness computation when current-when is specified'](
      assert
    ) {
      let appLink;

      this.router.map(function() {
        this.route('parent');
      });
      this.addTemplate(
        'application',
        `
      {{#link-to 'parent' (query-params page=1) current-when='parent' id='app-link'}}
        Parent
      {{/link-to}}
      {{outlet}}
    `
      );
      this.addTemplate(
        'parent',
        `
      {{#link-to 'parent' (query-params page=1) current-when='parent' id='parent-link'}}
        Parent
      {{/link-to}}
      {{outlet}}
    `
      );
      this.add(
        'controller:parent',
        Controller.extend({
          queryParams: ['page'],
          page: 1,
        })
      );

      return this.visit('/')
        .then(() => {
          appLink = this.$('#app-link');

          assert.equal(appLink.attr('href'), '/parent');
          this.shouldNotBeActive(assert, '#app-link');

          return this.visit('/parent?page=2');
        })
        .then(() => {
          appLink = this.$('#app-link');
          let router = this.appRouter;

          assert.equal(appLink.attr('href'), '/parent');
          this.shouldBeActive(assert, '#app-link');
          assert.equal(this.$('#parent-link').attr('href'), '/parent');
          this.shouldBeActive(assert, '#parent-link');

          let parentController = this.getController('parent');

          assert.equal(parentController.get('page'), 2);

          this.runTask(() => parentController.set('page', 3));

          assert.equal(router.get('location.path'), '/parent?page=3');
          this.shouldBeActive(assert, '#app-link');
          this.shouldBeActive(assert, '#parent-link');

          this.runTask(() => this.click('#app-link'));

          assert.equal(router.get('location.path'), '/parent');
        });
    }

    ['@test link-to default query params while in active transition regression test'](assert) {
      this.router.map(function() {
        this.route('foos');
        this.route('bars');
      });
      let foos = RSVP.defer();
      let bars = RSVP.defer();

      this.addTemplate(
        'application',
        `
      {{link-to 'Foos' 'foos' id='foos-link'}}
      {{link-to 'Baz Foos' 'foos' (query-params baz=true) id='baz-foos-link'}}
      {{link-to 'Quux Bars' 'bars' (query-params quux=true) id='bars-link'}}
    `
      );
      this.add(
        'controller:foos',
        Controller.extend({
          queryParams: ['status'],
          baz: false,
        })
      );
      this.add(
        'route:foos',
        Route.extend({
          model() {
            return foos.promise;
          },
        })
      );
      this.add(
        'controller:bars',
        Controller.extend({
          queryParams: ['status'],
          quux: false,
        })
      );
      this.add(
        'route:bars',
        Route.extend({
          model() {
            return bars.promise;
          },
        })
      );

      return this.visit('/').then(() => {
        let router = this.appRouter;
        let foosLink = this.$('#foos-link');
        let barsLink = this.$('#bars-link');
        let bazLink = this.$('#baz-foos-link');

        assert.equal(foosLink.attr('href'), '/foos');
        assert.equal(bazLink.attr('href'), '/foos?baz=true');
        assert.equal(barsLink.attr('href'), '/bars?quux=true');
        assert.equal(router.get('location.path'), '/');
        this.shouldNotBeActive(assert, '#foos-link');
        this.shouldNotBeActive(assert, '#baz-foos-link');
        this.shouldNotBeActive(assert, '#bars-link');

        this.runTask(() => barsLink.click());
        this.shouldNotBeActive(assert, '#bars-link');

        this.runTask(() => foosLink.click());
        this.shouldNotBeActive(assert, '#foos-link');

        this.runTask(() => foos.resolve());

        assert.equal(router.get('location.path'), '/foos');
        this.shouldBeActive(assert, '#foos-link');
      });
    }

    [`@test the {{link-to}} helper throws a useful error if you invoke it wrong`](assert) {
      assert.expect(1);

      this.addTemplate('application', `{{#link-to id='the-link'}}Index{{/link-to}}`);

      expectAssertion(() => {
        this.visit('/');
      }, /You must provide one or more parameters to the link-to component/);

      return this.runLoopSettled();
    }
  }
);
Пример #2
0
moduleFor(
  'ember reexports',
  class extends AbstractTestCase {
    [`@test Ember exports correctly`](assert) {
      allExports.forEach(reexport => {
        let [path, moduleId, exportName] = reexport;

        // default path === exportName if none present
        if (!exportName) {
          exportName = path;
        }

        confirmExport(Ember, assert, path, moduleId, exportName, `Ember.${path} exports correctly`);
      });
    }

    ['@test Ember.String.isHTMLSafe exports correctly'](assert) {
      confirmExport(Ember, assert, 'String.isHTMLSafe', '@ember/-internals/glimmer', 'isHTMLSafe');
    }

    ['@test Ember.EXTEND_PROTOTYPES is present (but deprecated)'](assert) {
      expectDeprecation(() => {
        assert.strictEqual(
          Ember.ENV.EXTEND_PROTOTYPES,
          Ember.EXTEND_PROTOTYPES,
          'Ember.EXTEND_PROTOTYPES exists'
        );
      }, /EXTEND_PROTOTYPES is deprecated/);
    }

    ['@test Ember.NAME_KEY is deprecated']() {
      expectDeprecation(() => {
        Ember.NAME_KEY;
      }, 'Using `Ember.NAME_KEY` is deprecated, override `.toString` instead');
    }

    '@test Ember.FEATURES is exported'(assert) {
      for (let feature in FEATURES) {
        assert.equal(
          Ember.FEATURES[feature],
          FEATURES[feature],
          'Ember.FEATURES contains ${feature} with correct value'
        );
      }
    }
  }
);
Пример #3
0
import { setProperties } from '..';
import { moduleFor, AbstractTestCase } from 'internal-test-helpers';

moduleFor('setProperties', class extends AbstractTestCase {
  ['@test supports setting multiple attributes at once'](assert) {
    assert.deepEqual(setProperties(null, null), null, 'noop for null properties and null object');
    assert.deepEqual(setProperties(undefined, undefined), undefined, 'noop for undefined properties and undefined object');

    assert.deepEqual(setProperties({}), undefined, 'noop for no properties');
    assert.deepEqual(setProperties({}, undefined), undefined, 'noop for undefined');
    assert.deepEqual(setProperties({}, null), null, 'noop for null');
    assert.deepEqual(setProperties({}, NaN), NaN, 'noop for NaN');
    assert.deepEqual(setProperties({}, {}), {}, 'meh');

    assert.deepEqual(setProperties({}, { foo: 1 }), { foo: 1 }, 'Set a single property');

    assert.deepEqual(setProperties({}, { foo: 1, bar: 1 }), { foo: 1, bar: 1 }, 'Set multiple properties');

    assert.deepEqual(setProperties({ foo: 2, baz: 2 }, { foo: 1 }), { foo: 1 }, 'Set one of multiple properties');

    assert.deepEqual(setProperties({ foo: 2, baz: 2 }, { bar: 2 }), {
      bar: 2
    }, 'Set an additional, previously unset property');
  }
});


Пример #4
0
moduleFor('Ember.Application Dependency Injection - Integration - default resolver', class extends DefaultResolverApplicationTestCase {

  beforeEach() {
    this.runTask(() => this.createApplication());
    return this.visit('/');
  }

  get privateRegistry() {
    return this.application.__registry__;
  }

  /*
   * This first batch of tests are integration tests against the public
   * applicationInstance API.
   */

  [`@test the default resolver looks up templates in Ember.TEMPLATES`](assert) {
    let fooTemplate = this.addTemplate('foo', `foo template`);
    let fooBarTemplate = this.addTemplate('fooBar', `fooBar template`);
    let fooBarBazTemplate = this.addTemplate('fooBar/baz', `fooBar/baz template`);

    assert.equal(
      this.applicationInstance.factoryFor('template:foo').class, fooTemplate,
      'resolves template:foo'
    );
    assert.equal(
      this.applicationInstance.factoryFor('template:fooBar').class, fooBarTemplate,
      'resolves template:foo_bar'
    );
    assert.equal(
      this.applicationInstance.factoryFor('template:fooBar.baz').class, fooBarBazTemplate,
      'resolves template:foo_bar.baz'
    );
  }

  [`@test the default resolver looks up basic name as no prefix`](assert) {
    let instance = this.applicationInstance.lookup('controller:basic');
    assert.ok(
      Controller.detect(instance),
      'locator looks up correct controller'
    );
  }

  [`@test the default resolver looks up arbitrary types on the namespace`](assert) {
    let Class = this.application.FooManager = EmberObject.extend();
    let resolvedClass = this.application.resolveRegistration('manager:foo');
    assert.equal(
      Class, resolvedClass,
      'looks up FooManager on application'
    );
  }

  [`@test the default resolver resolves models on the namespace`](assert) {
    let Class = this.application.Post = EmberObject.extend();
    let factoryClass = this.applicationInstance.factoryFor('model:post').class;
    assert.equal(
      Class, factoryClass,
      'looks up Post model on application'
    );
  }

  [`@test the default resolver resolves *:main on the namespace`](assert) {
    let Class = this.application.FooBar = EmberObject.extend();
    let factoryClass = this.applicationInstance.factoryFor('foo-bar:main').class;
    assert.equal(
      Class, factoryClass,
      'looks up FooBar type without name on application'
    );
  }

  [`@test the default resolver resolves container-registered helpers`](assert) {
    let shorthandHelper = makeHelper(() => {});
    let helper = Helper.extend();

    this.application.register('helper:shorthand', shorthandHelper);
    this.application.register('helper:complete', helper);

    let lookedUpShorthandHelper = this.applicationInstance.factoryFor('helper:shorthand').class;

    assert.ok(lookedUpShorthandHelper.isHelperInstance, 'shorthand helper isHelper');

    let lookedUpHelper = this.applicationInstance.factoryFor('helper:complete').class;

    assert.ok(lookedUpHelper.isHelperFactory, 'complete helper is factory');
    assert.ok(helper.detect(lookedUpHelper), 'looked up complete helper');
  }

  [`@test the default resolver resolves container-registered helpers via lookupFor`](assert) {
    let shorthandHelper = makeHelper(() => {});
    let helper = Helper.extend();

    this.application.register('helper:shorthand', shorthandHelper);
    this.application.register('helper:complete', helper);

    let lookedUpShorthandHelper = this.applicationInstance.factoryFor('helper:shorthand').class;

    assert.ok(lookedUpShorthandHelper.isHelperInstance, 'shorthand helper isHelper');

    let lookedUpHelper = this.applicationInstance.factoryFor('helper:complete').class;

    assert.ok(lookedUpHelper.isHelperFactory, 'complete helper is factory');
    assert.ok(helper.detect(lookedUpHelper), 'looked up complete helper');
  }

  [`@test the default resolver resolves helpers on the namespace`](assert) {
    let ShorthandHelper = makeHelper(() =>  {});
    let CompleteHelper = Helper.extend();

    this.application.ShorthandHelper = ShorthandHelper;
    this.application.CompleteHelper = CompleteHelper;

    let resolvedShorthand = this.application.resolveRegistration('helper:shorthand');
    let resolvedComplete = this.application.resolveRegistration('helper:complete');

    assert.equal(resolvedShorthand, ShorthandHelper, 'resolve fetches the shorthand helper factory');
    assert.equal(resolvedComplete, CompleteHelper, 'resolve fetches the complete helper factory');
  }

  [`@test the default resolver resolves to the same instance, no matter the notation `](assert) {
    this.application.NestedPostController = Controller.extend({});

    assert.equal(
      this.applicationInstance.lookup('controller:nested-post'),
      this.applicationInstance.lookup('controller:nested_post'),
      'looks up NestedPost controller on application'
    );
  }

  [`@test the default resolver throws an error if the fullName to resolve is invalid`](assert) {
    assert.throws(() => { this.applicationInstance.resolveRegistration(undefined);}, TypeError, /Invalid fullName/);
    assert.throws(() => { this.applicationInstance.resolveRegistration(null);     }, TypeError, /Invalid fullName/);
    assert.throws(() => { this.applicationInstance.resolveRegistration('');       }, TypeError, /Invalid fullName/);
    assert.throws(() => { this.applicationInstance.resolveRegistration('');       }, TypeError, /Invalid fullName/);
    assert.throws(() => { this.applicationInstance.resolveRegistration(':');      }, TypeError, /Invalid fullName/);
    assert.throws(() => { this.applicationInstance.resolveRegistration('model');  }, TypeError, /Invalid fullName/);
    assert.throws(() => { this.applicationInstance.resolveRegistration('model:'); }, TypeError, /Invalid fullName/);
    assert.throws(() => { this.applicationInstance.resolveRegistration(':type');  }, TypeError, /Invalid fullName/);
  }

  /*
   * The following are integration tests against the private registry API.
   */

  [`@test lookup description`](assert) {
    this.application.toString = () => 'App';

    assert.equal(
      this.privateRegistry.describe('controller:foo'), 'App.FooController',
      'Type gets appended at the end'
    );
    assert.equal(
      this.privateRegistry.describe('controller:foo.bar'), 'App.FooBarController',
      'dots are removed'
    );
    assert.equal(
      this.privateRegistry.describe('model:foo'), 'App.Foo',
      'models don\'t get appended at the end'
    );
  }

  [`@test assertion for routes without isRouteFactory property`](assert) {
    this.application.FooRoute = Component.extend();

    expectAssertion(() => {
      this.privateRegistry.resolve(`route:foo`)
    }, /to resolve to an Ember.Route/, 'Should assert');
  }

  [`@test no assertion for routes that extend from Ember.Route`](assert) {
    assert.expect(0);
    this.application.FooRoute = Route.extend();
    this.privateRegistry.resolve(`route:foo`);
  }

  [`@test deprecation warning for service factories without isServiceFactory property`](assert) {
    expectAssertion(() =>{
      this.application.FooService = EmberObject.extend();
      this.privateRegistry.resolve('service:foo');
    }, /Expected service:foo to resolve to an Ember.Service but instead it was \.FooService\./);
  }

  [`@test no deprecation warning for service factories that extend from Ember.Service`](assert) {
    assert.expect(0);
    this.application.FooService = Service.extend();
    this.privateRegistry.resolve('service:foo');
  }

  [`@test deprecation warning for component factories without isComponentFactory property`](assert) {
    expectAssertion(() => {
      this.application.FooComponent = EmberObject.extend();
      this.privateRegistry.resolve('component:foo');
    }, /Expected component:foo to resolve to an Ember\.Component but instead it was \.FooComponent\./);
  }

  [`@test no deprecation warning for component factories that extend from Ember.Component`](assert) {
    expectNoDeprecation();
    this.application.FooView = Component.extend();
    this.privateRegistry.resolve('component:foo');
  }

  [`@test knownForType returns each item for a given type found`](assert) {
    this.application.FooBarHelper = 'foo';
    this.application.BazQuxHelper = 'bar';

    let found = this.privateRegistry.resolver.knownForType('helper');

    assert.deepEqual(found, {
      'helper:foo-bar': true,
      'helper:baz-qux': true
    });
  }

  [`@test knownForType is not required to be present on the resolver`](assert) {
    delete this.privateRegistry.resolver.knownForType;

    this.privateRegistry.resolver.knownForType('helper', () => { });

    assert.ok(true, 'does not error');
  }

});
Пример #5
0
moduleFor('Application - visit()', class extends ApplicationTestCase {

  teardown() {
    RSVP.on('error', onerrorDefault);
    ENV._APPLICATION_TEMPLATE_WRAPPER = false;
    super.teardown();
  }

  createApplication(options) {
    return super.createApplication(options, Application.extend());
  }

  assertEmptyFixture(message) {
    this.assert.strictEqual(
      document.getElementById('qunit-fixture').children.length, 0,
      `there are no elements in the fixture element ${message ? message : ''}`
    );
  }

  [`@test does not add serialize-mode markers by default`](assert) {
    let templateContent = '<div class="foo">Hi, Mom!</div>';
    this.addTemplate('index', templateContent);
    let rootElement = document.createElement('div');

    let bootOptions = {
      isBrowser: false,
      rootElement
    };

    ENV._APPLICATION_TEMPLATE_WRAPPER = false;
    return this.visit('/', bootOptions).then(()=> {
      assert.equal(rootElement.innerHTML, templateContent, 'without serialize flag renders as expected');
    });
  }

  [`@test _renderMode: rehydration`](assert) {
    assert.expect(2);

    let indexTemplate = '<div class="foo">Hi, Mom!</div>';
    this.addTemplate('index', indexTemplate);
    let rootElement = document.createElement('div');

    let bootOptions = {
      isBrowser: false,
      rootElement,
      _renderMode: 'serialize'
    };

    ENV._APPLICATION_TEMPLATE_WRAPPER = false;

    return this.visit('/', bootOptions)
      .then((instance) => {
        assert.ok(
          isSerializationFirstNode(instance.rootElement.firstChild),
          'glimmer-vm comment node was not found'
        );
      }).then(() =>{
        return this.runTask(()=>{
          this.applicationInstance.destroy();
          this.applicationInstance = null;
        });
      }).then(() => {
        bootOptions = {
          isBrowser: false,
          rootElement,
          _renderMode: 'rehydrate'
        };

        this.application.visit('/', bootOptions).then(instance => {
          assert.equal(
            instance.rootElement.innerHTML,
            indexTemplate,
            'was not properly rehydrated'
          );
        });
      });
  }

  // This tests whether the application is "autobooted" by registering an
  // instance initializer and asserting it never gets run. Since this is
  // inherently testing that async behavior *doesn't* happen, we set a
  // 500ms timeout to verify that when autoboot is set to false, the
  // instance initializer that would normally get called on DOM ready
  // does not fire.
  [`@test Applications with autoboot set to false do not autoboot`](assert) {
    function delay(time) {
      return new RSVP.Promise(resolve => run.later(resolve, time));
    }

    let appBooted = 0;
    let instanceBooted = 0;

    this.application.initializer({
      name: 'assert-no-autoboot',
      initialize() {
        appBooted++;
      }
    });

    this.application.instanceInitializer({
      name: 'assert-no-autoboot',
      initialize() {
        instanceBooted++;
      }
    });

    assert.ok(!this.applicationInstance, 'precond - no instance');
    assert.ok(appBooted === 0, 'precond - not booted');
    assert.ok(instanceBooted === 0, 'precond - not booted');

    // Continue after 500ms
    return delay(500).then(() => {
      assert.ok(appBooted === 0, '500ms elapsed without app being booted');
      assert.ok(instanceBooted === 0, '500ms elapsed without instances being booted');

      return this.runTask(() => this.application.boot());
    }).then(() => {
      assert.ok(appBooted === 1, 'app should boot when manually calling `app.boot()`');
      assert.ok(instanceBooted === 0, 'no instances should be booted automatically when manually calling `app.boot()');
    });
  }

  [`@test calling visit() on an app without first calling boot() should boot the app`](assert) {
    let appBooted = 0;
    let instanceBooted = 0;

    this.application.initializer({
      name: 'assert-no-autoboot',
      initialize() {
        appBooted++;
      }
    });

    this.application.instanceInitializer({
      name: 'assert-no-autoboot',
      initialize() {
        instanceBooted++;
      }
    });

    return this.visit('/').then(() => {
      assert.ok(appBooted === 1, 'the app should be booted`');
      assert.ok(instanceBooted === 1, 'an instances should be booted');
    });
  }

  [`@test calling visit() on an already booted app should not boot it again`](assert) {
    let appBooted = 0;
    let instanceBooted = 0;

    this.application.initializer({
      name: 'assert-no-autoboot',
      initialize() {
        appBooted++;
      }
    });

    this.application.instanceInitializer({
      name: 'assert-no-autoboot',
      initialize() {
        instanceBooted++;
      }
    });

    return this.runTask(() => this.application.boot()).then(() => {
      assert.ok(appBooted === 1, 'the app should be booted');
      assert.ok(instanceBooted === 0, 'no instances should be booted');

      return this.visit('/');
    }).then(() => {
      assert.ok(appBooted === 1, 'the app should not be booted again');
      assert.ok(instanceBooted === 1, 'an instance should be booted');

      /*
       * Destroy the instance.
       */
      return this.runTask(() => {
        this.applicationInstance.destroy();
        this.applicationInstance = null;
      });
    }).then(() => {
      /*
       * Visit on the application a second time. The application should remain
       * booted, but a new instance will be created.
       */
      return this.application.visit('/').then(instance => {
        this.applicationInstance = instance;
      });
    }).then(() => {
      assert.ok(appBooted === 1, 'the app should not be booted again');
      assert.ok(instanceBooted === 2, 'another instance should be booted');
    });
  }

  [`@test visit() rejects on application boot failure`](assert) {
    this.application.initializer({
      name: 'error',
      initialize() {
        throw new Error('boot failure');
      }
    });

    expectAsyncError();

    return this.visit('/').then(() => {
      assert.ok(false, 'It should not resolve the promise');
    }, error => {
      assert.ok(error instanceof Error, 'It should reject the promise with the boot error');
      assert.equal(error.message, 'boot failure');
    });
  }

  [`@test visit() rejects on instance boot failure`](assert) {
    this.application.instanceInitializer({
      name: 'error',
      initialize() {
        throw new Error('boot failure');
      }
    });

    expectAsyncError();

    return this.visit('/').then(() => {
      assert.ok(false, 'It should not resolve the promise');
    }, error => {
      assert.ok(error instanceof Error, 'It should reject the promise with the boot error');
      assert.equal(error.message, 'boot failure');
    });
  }

  [`@test visit() follows redirects`](assert) {
    this.router.map(function() {
      this.route('a');
      this.route('b', { path: '/b/:b' });
      this.route('c', { path: '/c/:c' });
    });

    this.add('route:a', Route.extend({
      afterModel() {
        this.replaceWith('b', 'zomg');
      }
    }));

    this.add('route:b', Route.extend({
      afterModel(params) {
        this.transitionTo('c', params.b);
      }
    }));

    /*
     * First call to `visit` is `this.application.visit` and returns the
     * applicationInstance.
     */
    return this.visit('/a').then(instance => {
      assert.ok(instance instanceof ApplicationInstance, 'promise is resolved with an ApplicationInstance');
      assert.equal(instance.getURL(), '/c/zomg', 'It should follow all redirects');
    });
  }

  [`@test visit() rejects if an error occurred during a transition`](assert) {
    this.router.map(function() {
      this.route('a');
      this.route('b', { path: '/b/:b' });
      this.route('c', { path: '/c/:c' });
    });

    this.add('route:a', Route.extend({
      afterModel() {
        this.replaceWith('b', 'zomg');
      }
    }));

    this.add('route:b', Route.extend({
      afterModel(params) {
        this.transitionTo('c', params.b);
      }
    }));

    this.add('route:c', Route.extend({
      afterModel() {
        throw new Error('transition failure');
      }
    }));

    expectAsyncError();

    return this.visit('/a').then(() => {
      assert.ok(false, 'It should not resolve the promise');
    }, error => {
      assert.ok(error instanceof Error, 'It should reject the promise with the boot error');
      assert.equal(error.message, 'transition failure');
    });
  }

  [`@test visit() chain`](assert) {
    this.router.map(function() {
      this.route('a');
      this.route('b');
      this.route('c');
    });

    return this.visit('/').then(instance => {
      assert.ok(instance instanceof ApplicationInstance, 'promise is resolved with an ApplicationInstance');
      assert.equal(instance.getURL(), '/');

      return instance.visit('/a');
    }).then(instance => {
      assert.ok(instance instanceof ApplicationInstance, 'promise is resolved with an ApplicationInstance');
      assert.equal(instance.getURL(), '/a');

      return instance.visit('/b');
    }).then(instance => {
      assert.ok(instance instanceof ApplicationInstance, 'promise is resolved with an ApplicationInstance');
      assert.equal(instance.getURL(), '/b');

      return instance.visit('/c');
    }).then(instance => {
      assert.ok(instance instanceof ApplicationInstance, 'promise is resolved with an ApplicationInstance');
      assert.equal(instance.getURL(), '/c');
    });
  }

  [`@test visit() returns a promise that resolves when the view has rendered`](assert) {
    this.addTemplate('application', `<h1>Hello world</h1>`);

    this.assertEmptyFixture();

    return this.visit('/').then(instance => {
      assert.ok(
        instance instanceof ApplicationInstance,
        'promise is resolved with an ApplicationInstance'
      );
      assert.equal(
        this.element.textContent, 'Hello world',
        'the application was rendered once the promise resolves'
      );
    });
  }

  [`@test visit() returns a promise that resolves without rendering when shouldRender is set to false`](assert) {
    assert.expect(3);

    this.addTemplate('application', '<h1>Hello world</h1>');

    this.assertEmptyFixture();

    return this.visit('/', { shouldRender: false }).then(instance => {
      assert.ok(
        instance instanceof ApplicationInstance,
        'promise is resolved with an ApplicationInstance'
      );

      this.assertEmptyFixture('after visit');
    });
  }

  [`@test visit() renders a template when shouldRender is set to true`](assert) {
    assert.expect(3);

    this.addTemplate('application', '<h1>Hello world</h1>');

    this.assertEmptyFixture();

    return this.visit('/', { shouldRender: true }).then(instance => {
      assert.ok(
        instance instanceof ApplicationInstance,
        'promise is resolved with an ApplicationInstance'
      );
      assert.strictEqual(
        document.querySelector('#qunit-fixture').children.length, 1,
        'there is 1 element in the fixture element after visit'
      );
    });
  }

  [`@test visit() returns a promise that resolves without rendering when shouldRender is set to false with Engines`](assert) {
    assert.expect(3);

    this.router.map(function() {
      this.mount('blog');
    });

    this.addTemplate('application', '<h1>Hello world</h1>');

    // Register engine
    let BlogEngine = Engine.extend();
    this.add('engine:blog', BlogEngine);

    // Register engine route map
    let BlogMap = function() {};
    this.add('route-map:blog', BlogMap);

    this.assertEmptyFixture();

    return this.visit('/blog', { shouldRender: false }).then(instance => {
      assert.ok(
        instance instanceof ApplicationInstance,
        'promise is resolved with an ApplicationInstance'
      );

      this.assertEmptyFixture('after visit');
    });
  }

  [`@test visit() does not setup the event_dispatcher:main if isInteractive is false (with Engines) GH#15615`](assert) {
    assert.expect(3);

    this.router.map(function() {
      this.mount('blog');
    });

    this.addTemplate('application', '<h1>Hello world</h1>{{outlet}}');
    this.add('event_dispatcher:main', {
      create() { throw new Error('should not happen!'); }
    });

    // Register engine
    let BlogEngine = Engine.extend({
      init(...args) {
        this._super.apply(this, args);
        this.register('template:application', compile('{{cache-money}}'));
        this.register('template:components/cache-money', compile(`
          <p>Dis cache money</p>
        `));
        this.register('component:cache-money', Component.extend({}));
      }
    });
    this.add('engine:blog', BlogEngine);

    // Register engine route map
    let BlogMap = function() {};
    this.add('route-map:blog', BlogMap);

    this.assertEmptyFixture();

    return this.visit('/blog', { isInteractive: false }).then(instance => {
      assert.ok(
        instance instanceof ApplicationInstance,
        'promise is resolved with an ApplicationInstance'
      );
      assert.strictEqual(
        this.element.querySelector('p').textContent, 'Dis cache money',
        'Engine component is resolved'
      );
    });
  }

  [`@test visit() on engine resolves engine component`](assert) {
    assert.expect(2);

    this.router.map(function() {
      this.mount('blog');
    });

    // Register engine
    let BlogEngine = Engine.extend({
      init(...args) {
        this._super.apply(this, args);
        this.register('template:application', compile('{{cache-money}}'));
        this.register('template:components/cache-money', compile(`
          <p>Dis cache money</p>
        `));
        this.register('component:cache-money', Component.extend({}));
      }
    });
    this.add('engine:blog', BlogEngine);

    // Register engine route map
    let BlogMap = function() {};
    this.add('route-map:blog', BlogMap);

    this.assertEmptyFixture();

    return this.visit('/blog', { shouldRender: true }).then(() => {
      assert.strictEqual(
        this.element.querySelector('p').textContent, 'Dis cache money',
        'Engine component is resolved'
      );
    });
  }

  [`@test visit() on engine resolves engine helper`](assert) {
    assert.expect(2);

    this.router.map(function() {
      this.mount('blog');
    });

    // Register engine
    let BlogEngine = Engine.extend({
      init(...args) {
        this._super.apply(this, args);
        this.register('template:application', compile('{{swag}}'));
        this.register('helper:swag', helper(function() {
          return 'turnt up';
        }));
      }
    });
    this.add('engine:blog', BlogEngine);

    // Register engine route map
    let BlogMap = function() {};
    this.add('route-map:blog', BlogMap);

    this.assertEmptyFixture();

    return this.visit('/blog', { shouldRender: true }).then(() => {
      assert.strictEqual(
        this.element.textContent, 'turnt up',
        'Engine component is resolved'
      );
    });
  }

  [`@test Ember Islands-style setup`](assert) {
    let xFooInitCalled = false;
    let xFooDidInsertElementCalled = false;

    let xBarInitCalled = false;
    let xBarDidInsertElementCalled = false;

    this.router.map(function() {
      this.route('show', { path: '/:component_name' });
    });

    this.add('route:show', Route.extend({
      queryParams: {
        data: { refreshModel: true }
      },

      model(params) {
        return {
          componentName: params.component_name,
          componentData: params.data ? JSON.parse(params.data) : undefined
        };
      }
    }));

    let Counter = EmberObject.extend({
      value: 0,

      increment() {
        this.incrementProperty('value');
      }
    });

    this.add('service:isolatedCounter', Counter);
    this.add('service:sharedCounter', Counter.create());
    this.application.registerOptions('service:sharedCounter', {instantiate: false});

    this.addTemplate('show', '{{component model.componentName model=model.componentData}}');

    this.addTemplate('components/x-foo', `
      <h1>X-Foo</h1>
      <p>Hello {{model.name}}, I have been clicked {{isolatedCounter.value}} times ({{sharedCounter.value}} times combined)!</p>
    `);

    this.add('component:x-foo', Component.extend({
      tagName: 'x-foo',

      isolatedCounter: inject.service(),
      sharedCounter: inject.service(),

      init() {
        this._super();
        xFooInitCalled = true;
      },

      didInsertElement() {
        xFooDidInsertElementCalled = true;
      },

      click() {
        this.get('isolatedCounter').increment();
        this.get('sharedCounter').increment();
      }
    }));

    this.addTemplate('components/x-bar', `
      <h1>X-Bar</h1>
      <button {{action "incrementCounter"}}>Join {{counter.value}} others in clicking me!</button>
    `);

    this.add('component:x-bar', Component.extend({
      counter: inject.service('sharedCounter'),

      actions: {
        incrementCounter() {
          this.get('counter').increment();
        }
      },

      init() {
        this._super();
        xBarInitCalled = true;
      },

      didInsertElement() {
        xBarDidInsertElementCalled = true;
      }
    }));

    let fixtureElement = document.querySelector('#qunit-fixture');
    let foo = document.createElement('div');
    let bar = document.createElement('div');
    fixtureElement.appendChild(foo);
    fixtureElement.appendChild(bar);

    let data = encodeURIComponent(JSON.stringify({ name: 'Godfrey' }));
    let instances = [];

    return RSVP.all([
      this.runTask(() => {
        return this.application.visit(`/x-foo?data=${data}`, { rootElement: foo });
      }),
      this.runTask(() => {
        return this.application.visit('/x-bar', { rootElement: bar });
      })
    ]).then(_instances => {
      instances = _instances;

      assert.ok(xFooInitCalled);
      assert.ok(xFooDidInsertElementCalled);

      assert.ok(xBarInitCalled);
      assert.ok(xBarDidInsertElementCalled);

      assert.equal(foo.querySelector('h1').textContent, 'X-Foo');
      assert.equal(foo.querySelector('p').textContent, 'Hello Godfrey, I have been clicked 0 times (0 times combined)!');
      assert.ok(foo.textContent.indexOf('X-Bar') === -1);

      assert.equal(bar.querySelector('h1').textContent, 'X-Bar');
      assert.equal(bar.querySelector('button').textContent, 'Join 0 others in clicking me!');
      assert.ok(bar.textContent.indexOf('X-Foo') === -1);

      this.runTask(() => {
        this.click(foo.querySelector('x-foo'));
      });

      assert.equal(foo.querySelector('p').textContent, 'Hello Godfrey, I have been clicked 1 times (1 times combined)!');
      assert.equal(bar.querySelector('button').textContent, 'Join 1 others in clicking me!');

      this.runTask(() => {
        this.click(bar.querySelector('button'));
        this.click(bar.querySelector('button'));
      });

      assert.equal(foo.querySelector('p').textContent, 'Hello Godfrey, I have been clicked 1 times (3 times combined)!');
      assert.equal(bar.querySelector('button').textContent, 'Join 3 others in clicking me!');

    }).finally(() => {
      this.runTask(() => {
        instances.forEach(instance => {
          instance.destroy();
        });
      });
    });
  }

});
Пример #6
0
moduleFor(
  'Ember.CoreObject',
  class extends AbstractTestCase {
    ['@test throws an error with new (one arg)']() {
      expectAssertion(() => {
        new CoreObject({
          firstName: 'Stef',
          lastName: 'Penner',
        });
      }, /You may have either used `new` instead of `.create\(\)`/);
    }

    ['@test throws an error with new (> 1 arg)']() {
      expectAssertion(() => {
        new CoreObject(
          {
            firstName: 'Stef',
            lastName: 'Penner',
          },
          {
            other: 'name',
          }
        );
      }, /You may have either used `new` instead of `.create\(\)`/);
    }

    ['@test toString should be not be added as a property when calling toString()'](assert) {
      let obj = CoreObject.create({
        firstName: 'Foo',
        lastName: 'Bar',
      });

      obj.toString();

      assert.notOk(
        obj.hasOwnProperty('toString'),
        'Calling toString() should not create a toString class property'
      );
    }

    ['@test should not trigger proxy assertion when retrieving a proxy with (GH#16263)'](assert) {
      let someProxyishThing = CoreObject.extend({
        unknownProperty() {
          return true;
        },
      }).create();

      let obj = CoreObject.create({
        someProxyishThing,
      });

      let proxy = get(obj, 'someProxyishThing');
      assert.equal(get(proxy, 'lolol'), true, 'should be able to get data from a proxy');
    }

    ['@test should not trigger proxy assertion when retrieving a re-registered proxy (GH#16610)'](
      assert
    ) {
      let owner = buildOwner();

      let someProxyishThing = CoreObject.extend({
        unknownProperty() {
          return true;
        },
      }).create();

      // emulates ember-engines's process of registering services provided
      // by the host app down to the engine
      owner.register('thing:one', someProxyishThing, { instantiate: false });

      assert.equal(owner.lookup('thing:one'), someProxyishThing);
    }

    ['@test should not trigger proxy assertion when probing for a "symbol"'](assert) {
      let proxy = CoreObject.extend({
        unknownProperty() {
          return true;
        },
      }).create();

      assert.equal(get(proxy, 'lolol'), true, 'should be able to get data from a proxy');

      // should not trigger an assertion
      getOwner(proxy);
    }

    ['@test can use getOwner in a proxy init GH#16484'](assert) {
      let owner = {};
      let options = {};
      setOwner(options, owner);

      CoreObject.extend({
        init() {
          this._super(...arguments);
          let localOwner = getOwner(this);

          assert.equal(localOwner, owner, 'should be able to `getOwner` in init');
        },
        unknownProperty() {
          return undefined;
        },
      }).create(options);
    }

    ['@test observed properties are enumerable when set GH#14594'](assert) {
      let callCount = 0;
      let Test = CoreObject.extend({
        myProp: null,
        anotherProp: undefined,
        didChangeMyProp: observer('myProp', function() {
          callCount++;
        }),
      });

      let test = Test.create();
      set(test, 'id', '3');
      set(test, 'myProp', { id: 1 });

      assert.deepEqual(Object.keys(test).sort(), ['id', 'myProp']);

      set(test, 'anotherProp', 'nice');

      assert.deepEqual(Object.keys(test).sort(), ['anotherProp', 'id', 'myProp']);

      assert.equal(callCount, 1);
    }
  }
);
Пример #7
0
moduleFor(
  'ember-testing Integration tests of acceptance',
  class extends AutobootApplicationTestCase {
    constructor() {
      super();

      this.modelContent = [];
      this._originalAdapter = Test.adapter;

      runTask(() => {
        this.createApplication();

        this.addTemplate(
          'people',
          `
        <div>
          {{#each model as |person|}}
            <div class="name">{{person.firstName}}</div>
          {{/each}}
        </div>
      `
        );

        this.router.map(function() {
          this.route('people', { path: '/' });
        });

        this.add(
          'route:people',
          Route.extend({
            model: () => this.modelContent,
          })
        );

        this.application.setupForTesting();
      });

      runTask(() => {
        this.application.reset();
      });

      this.application.injectTestHelpers();
    }

    teardown() {
      super.teardown();
      Test.adapter = this._originalAdapter;
    }

    [`@test template is bound to empty array of people`](assert) {
      if (!jQueryDisabled) {
        runTask(() => this.application.advanceReadiness());
        window.visit('/').then(() => {
          let rows = window.find('.name').length;
          assert.equal(rows, 0, 'successfully stubbed an empty array of people');
        });
      } else {
        runTask(() => this.application.advanceReadiness());
        window.visit('/').then(() => {
          expectAssertion(
            () => window.find('.name'),
            'If jQuery is disabled, please import and use helpers from @ember/test-helpers [https://github.com/emberjs/ember-test-helpers]. Note: `find` is not an available helper.'
          );
        });
      }
    }

    [`@test template is bound to array of 2 people`](assert) {
      if (!jQueryDisabled) {
        this.modelContent = emberA([]);
        this.modelContent.pushObject({ firstName: 'x' });
        this.modelContent.pushObject({ firstName: 'y' });

        runTask(() => this.application.advanceReadiness());
        window.visit('/').then(() => {
          let rows = window.find('.name').length;
          assert.equal(rows, 2, 'successfully stubbed a non empty array of people');
        });
      } else {
        assert.expect(0);
      }
    }

    [`@test 'visit' can be called without advanceReadiness.`](assert) {
      if (!jQueryDisabled) {
        window.visit('/').then(() => {
          let rows = window.find('.name').length;
          assert.equal(
            rows,
            0,
            'stubbed an empty array of people without calling advanceReadiness.'
          );
        });
      } else {
        assert.expect(0);
      }
    }
  }
);
Пример #8
0
moduleFor(
  'get',
  class extends AbstractTestCase {
    ['@test should get arbitrary properties on an object'](assert) {
      let obj = {
        string: 'string',
        number: 23,
        boolTrue: true,
        boolFalse: false,
        nullValue: null,
      };

      for (let key in obj) {
        if (!obj.hasOwnProperty(key)) {
          continue;
        }
        assert.equal(get(obj, key), obj[key], key);
      }
    }

    ['@test implicitly computing the values of descriptors on properties is deprecated'](assert) {
      let cp = computed(() => 'value');
      let obj = {
        cp,
        toString() {
          return 'myobject';
        },
      };

      let result;

      expectDeprecation(() => {
        result = get(obj, 'cp');
      }, /\[DEPRECATED\] computed property 'cp' was not set on object 'myobject' via 'defineProperty'/);

      assert.equal(result, 'value', 'descriptor');
    }

    ['@test should retrieve a number key on an object'](assert) {
      let obj = { 1: 'first' };

      assert.equal(get(obj, 1), 'first');
    }

    ['@test should retrieve an array index'](assert) {
      let arr = ['first', 'second'];

      assert.equal(get(arr, 0), 'first');
      assert.equal(get(arr, 1), 'second');
    }

    ['@test should not access a property more than once'](assert) {
      let count = 0;
      let obj = {
        get id() {
          return ++count;
        },
      };

      get(obj, 'id');

      assert.equal(count, 1);
    }

    ['@test should call unknownProperty on watched values if the value is undefined using getFromEmberMetal()/set()'](
      assert
    ) {
      let obj = {
        unknownProperty(key) {
          assert.equal(key, 'foo', 'should pass key');
          return 'FOO';
        },
      };
      assert.equal(get(obj, 'foo'), 'FOO', 'should return value from unknown');
    }

    ['@test should call unknownProperty on watched values if the value is undefined using accessors'](
      assert
    ) {
      if (ENV.USES_ACCESSORS) {
        let obj = {
          unknownProperty(key) {
            assert.equal(key, 'foo', 'should pass key');
            return 'FOO';
          },
        };
        assert.equal(aget(obj, 'foo'), 'FOO', 'should return value from unknown');
      } else {
        assert.ok('SKIPPING ACCESSORS');
      }
    }

    ['@test warn on attempts to call get with no arguments']() {
      expectAssertion(function() {
        get('aProperty');
      }, /Get must be called with two arguments;/i);
    }

    ['@test warn on attempts to call get with only one argument']() {
      expectAssertion(function() {
        get('aProperty');
      }, /Get must be called with two arguments;/i);
    }

    ['@test warn on attempts to call get with more then two arguments']() {
      expectAssertion(function() {
        get({}, 'aProperty', true);
      }, /Get must be called with two arguments;/i);
    }

    ['@test warn on attempts to get a property of undefined']() {
      expectAssertion(function() {
        get(undefined, 'aProperty');
      }, /Cannot call get with 'aProperty' on an undefined object/i);
    }

    ['@test warn on attempts to get a property path of undefined']() {
      expectAssertion(function() {
        get(undefined, 'aProperty.on.aPath');
      }, /Cannot call get with 'aProperty.on.aPath' on an undefined object/);
    }

    ['@test warn on attempts to get a property of null']() {
      expectAssertion(function() {
        get(null, 'aProperty');
      }, /Cannot call get with 'aProperty' on an undefined object/);
    }

    ['@test warn on attempts to get a property path of null']() {
      expectAssertion(function() {
        get(null, 'aProperty.on.aPath');
      }, /Cannot call get with 'aProperty.on.aPath' on an undefined object/);
    }

    ['@test warn on attempts to use get with an unsupported property path']() {
      let obj = {};
      expectAssertion(
        () => get(obj, null),
        /The key provided to get must be a string or number, you passed null/
      );
      expectAssertion(
        () => get(obj, NaN),
        /The key provided to get must be a string or number, you passed NaN/
      );
      expectAssertion(
        () => get(obj, undefined),
        /The key provided to get must be a string or number, you passed undefined/
      );
      expectAssertion(
        () => get(obj, false),
        /The key provided to get must be a string or number, you passed false/
      );
      expectAssertion(() => get(obj, ''), /Cannot call `get` with an empty string/);
    }

    // ..........................................................
    // BUGS
    //

    ['@test (regression) watched properties on unmodified inherited objects should still return their original value'](
      assert
    ) {
      let MyMixin = Mixin.create({
        someProperty: 'foo',
        propertyDidChange: observer('someProperty', () => {}),
      });

      let baseObject = MyMixin.apply({});
      let theRealObject = Object.create(baseObject);

      assert.equal(
        get(theRealObject, 'someProperty'),
        'foo',
        'should return the set value, not false'
      );
    }
  }
);
Пример #9
0
moduleFor('Ember.Application with default resolver and autoboot', class extends ApplicationTestCase {
  constructor() {
    jQuery('#qunit-fixture').html(`
      <div id="app"></div>

      <script type="text/x-handlebars">Hello {{outlet}}</script>
      <script type="text/x-handlebars" id="index">World!</script>
    `);
    super();
  }

  teardown() {
    setTemplates({});
  }

  get applicationOptions() {
    return assign(super.applicationOptions, {
      autoboot: true,
      rootElement: '#app',
      Resolver: DefaultResolver,
      Router: Router.extend({
        location: 'none'
      })
    });
  }

  ['@test templates in script tags are extracted at application creation'](assert) {
    assert.equal(jQuery('#app').text(), 'Hello World!');
  }
});
Пример #10
0
moduleFor(
  'defineProperty',
  class extends AbstractTestCase {
    ['@test toString'](assert) {
      let obj = {};
      defineProperty(obj, 'toString', undefined, function() {
        return 'FOO';
      });
      assert.equal(obj.toString(), 'FOO', 'should replace toString');
    }

    ['@test for data properties, didDefineProperty hook should be called if implemented'](assert) {
      assert.expect(2);

      let obj = {
        didDefineProperty(obj, keyName, value) {
          assert.equal(keyName, 'foo', 'key name should be foo');
          assert.equal(value, 'bar', 'value should be bar');
        },
      };

      defineProperty(obj, 'foo', undefined, 'bar');
    }

    ['@test for computed properties, didDefineProperty hook should be called if implemented'](
      assert
    ) {
      assert.expect(2);

      let computedProperty = computed(function() {
        return this;
      });

      let obj = {
        didDefineProperty(obj, keyName, value) {
          assert.equal(keyName, 'foo', 'key name should be foo');
          assert.strictEqual(
            value,
            computedProperty,
            'value should be passed as computed property'
          );
        },
      };

      defineProperty(obj, 'foo', computedProperty);
    }

    ['@test for descriptor properties, didDefineProperty hook should be called if implemented'](
      assert
    ) {
      assert.expect(2);

      let descriptor = {
        writable: true,
        configurable: false,
        enumerable: true,
        value: 42,
      };

      let obj = {
        didDefineProperty(obj, keyName, value) {
          assert.equal(keyName, 'answer', 'key name should be answer');
          assert.strictEqual(value, descriptor, 'value should be passed as descriptor');
        },
      };

      defineProperty(obj, 'answer', descriptor);
    }
  }
);
Пример #11
0
moduleFor(
  'Controller event handling',
  class extends AbstractTestCase {
    ['@test Action can be handled by a function on actions object'](assert) {
      assert.expect(1);
      let TestController = Controller.extend({
        actions: {
          poke() {
            assert.ok(true, 'poked');
          },
        },
      });
      let controller = TestController.create();
      controller.send('poke');
    }

    ['@test A handled action can be bubbled to the target for continued processing'](assert) {
      assert.expect(2);
      let TestController = Controller.extend({
        actions: {
          poke() {
            assert.ok(true, 'poked 1');
            return true;
          },
        },
      });

      let controller = TestController.create({
        target: Controller.extend({
          actions: {
            poke() {
              assert.ok(true, 'poked 2');
            },
          },
        }).create(),
      });
      controller.send('poke');
    }

    ["@test Action can be handled by a superclass' actions object"](assert) {
      assert.expect(4);

      let SuperController = Controller.extend({
        actions: {
          foo() {
            assert.ok(true, 'foo');
          },
          bar(msg) {
            assert.equal(msg, 'HELLO');
          },
        },
      });

      let BarControllerMixin = Mixin.create({
        actions: {
          bar(msg) {
            assert.equal(msg, 'HELLO');
            this._super(msg);
          },
        },
      });

      let IndexController = SuperController.extend(BarControllerMixin, {
        actions: {
          baz() {
            assert.ok(true, 'baz');
          },
        },
      });

      let controller = IndexController.create({});
      controller.send('foo');
      controller.send('bar', 'HELLO');
      controller.send('baz');
    }

    ['@test .send asserts if called on a destroyed controller']() {
      let owner = buildOwner();

      owner.register(
        'controller:application',
        Controller.extend({
          toString() {
            return 'controller:rip-alley';
          },
        })
      );

      let controller = owner.lookup('controller:application');
      runDestroy(owner);

      expectAssertion(() => {
        controller.send('trigger-me-dead');
      }, "Attempted to call .send() with the action 'trigger-me-dead' on the destroyed object 'controller:rip-alley'.");
    }
  }
);
Пример #12
0
moduleFor(
  'Chains',
  class extends AbstractTestCase {
    ['@test finishChains should properly copy chains from prototypes to instances'](assert) {
      function didChange() {}

      let obj = {};
      addObserver(obj, 'foo.bar', null, didChange);

      let childObj = Object.create(obj);

      let parentMeta = meta(obj);
      let childMeta = meta(childObj);

      finishChains(childMeta);

      assert.ok(
        parentMeta.readableChains() !== childMeta.readableChains(),
        'The chains object is copied'
      );
    }

    ['@test does not observe primitive values'](assert) {
      let obj = {
        foo: { bar: 'STRING' },
      };

      addObserver(obj, 'foo.bar.baz', null, function() {});
      let meta = peekMeta(obj);
      assert.notOk(meta._object);
    }

    ['@test observer and CP chains'](assert) {
      let obj = {};

      defineProperty(obj, 'foo', computed('qux.[]', function() {}));
      defineProperty(obj, 'qux', computed(function() {}));

      // create DK chains
      get(obj, 'foo');

      // create observer chain
      addObserver(obj, 'qux.length', function() {});

      /*
             +-----+
             | qux |   root CP
             +-----+
                ^
         +------+-----+
         |            |
     +--------+    +----+
     | length |    | [] |  chainWatchers
     +--------+    +----+
      observer       CP(foo, 'qux.[]')
    */

      // invalidate qux
      notifyPropertyChange(obj, 'qux');

      // CP chain is blown away

      /*
             +-----+
             | qux |   root CP
             +-----+
                ^
         +------+xxxxxx
         |            x
     +--------+    xxxxxx
     | length |    x [] x  chainWatchers
     +--------+    xxxxxx
      observer       CP(foo, 'qux.[]')
    */

      get(obj, 'qux'); // CP chain re-recreated
      assert.ok(true, 'no crash');
    }

    ['@test checks cache correctly'](assert) {
      let obj = {};
      let parentChainNode = new ChainNode(null, null, obj);
      let chainNode = new ChainNode(parentChainNode, 'foo');

      defineProperty(
        obj,
        'foo',
        computed(function() {
          return undefined;
        })
      );
      get(obj, 'foo');

      assert.strictEqual(chainNode.value(), undefined);
    }

    ['@test chains are watched correctly'](assert) {
      let obj = { foo: { bar: { baz: 1 } } };

      watch(obj, 'foo.bar.baz');

      assert.equal(watcherCount(obj, 'foo'), 1);
      assert.equal(watcherCount(obj, 'foo.bar'), 0);
      assert.equal(watcherCount(obj, 'foo.bar.baz'), 1);
      assert.equal(watcherCount(obj.foo, 'bar'), 1);
      assert.equal(watcherCount(obj.foo, 'bar.baz'), 0);
      assert.equal(watcherCount(obj.foo.bar, 'baz'), 1);

      unwatch(obj, 'foo.bar.baz');

      assert.equal(watcherCount(obj, 'foo'), 0);
      assert.equal(watcherCount(obj, 'foo.bar'), 0);
      assert.equal(watcherCount(obj, 'foo.bar.baz'), 0);
      assert.equal(watcherCount(obj.foo, 'bar'), 0);
      assert.equal(watcherCount(obj.foo, 'bar.baz'), 0);
      assert.equal(watcherCount(obj.foo.bar, 'baz'), 0);
    }

    ['@test chains with single character keys are watched correctly'](assert) {
      let obj = { a: { b: { c: 1 } } };

      watch(obj, 'a.b.c');

      assert.equal(watcherCount(obj, 'a'), 1);
      assert.equal(watcherCount(obj, 'a.b'), 0);
      assert.equal(watcherCount(obj, 'a.b.c'), 1);
      assert.equal(watcherCount(obj.a, 'b'), 1);
      assert.equal(watcherCount(obj.a, 'b.c'), 0);
      assert.equal(watcherCount(obj.a.b, 'c'), 1);

      unwatch(obj, 'a.b.c');

      assert.equal(watcherCount(obj, 'a'), 0);
      assert.equal(watcherCount(obj, 'a.b'), 0);
      assert.equal(watcherCount(obj, 'a.b.c'), 0);
      assert.equal(watcherCount(obj.a, 'b'), 0);
      assert.equal(watcherCount(obj.a, 'b.c'), 0);
      assert.equal(watcherCount(obj.a.b, 'c'), 0);
    }
  }
);
Пример #13
0
moduleFor(
  'EmberStringUtils.classify',
  class extends AbstractTestCase {
    ['@test String.prototype.classify is not modified without EXTEND_PROTOTYPES'](assert) {
      if (!ENV.EXTEND_PROTOTYPES.String) {
        assert.ok(
          'undefined' === typeof String.prototype.classify,
          'String.prototype helper disabled'
        );
      } else {
        assert.expect(0);
      }
    }

    ['@test String classify tests'](assert) {
      test(assert, 'my favorite items', 'MyFavoriteItems', 'classify normal string');
      test(assert, 'css-class-name', 'CssClassName', 'classify dasherized string');
      test(assert, 'action_name', 'ActionName', 'classify underscored string');
      test(
        assert,
        'privateDocs/ownerInvoice',
        'PrivateDocs/OwnerInvoice',
        'classify namespaced camelized string'
      );
      test(
        assert,
        'private_docs/owner_invoice',
        'PrivateDocs/OwnerInvoice',
        'classify namespaced underscored string'
      );
      test(
        assert,
        'private-docs/owner-invoice',
        'PrivateDocs/OwnerInvoice',
        'classify namespaced dasherized string'
      );
      test(assert, '-view-registry', '_ViewRegistry', 'classify prefixed dasherized string');
      test(
        assert,
        'components/-text-field',
        'Components/_TextField',
        'classify namespaced prefixed dasherized string'
      );
      test(assert, '_Foo_Bar', '_FooBar', 'classify underscore-prefixed underscored string');
      test(assert, '_Foo-Bar', '_FooBar', 'classify underscore-prefixed dasherized string');
      test(
        assert,
        '_foo/_bar',
        '_Foo/_Bar',
        'classify underscore-prefixed-namespaced underscore-prefixed string'
      );
      test(
        assert,
        '-foo/_bar',
        '_Foo/_Bar',
        'classify dash-prefixed-namespaced underscore-prefixed string'
      );
      test(
        assert,
        '-foo/-bar',
        '_Foo/_Bar',
        'classify dash-prefixed-namespaced dash-prefixed string'
      );
      test(assert, 'InnerHTML', 'InnerHTML', 'does nothing with classified string');
      test(assert, '_FooBar', '_FooBar', 'does nothing with classified prefixed string');
    }
  }
);
moduleFor('Ember.Application instance initializers', class extends AutobootApplicationTestCase {
  constructor() {
    jQuery('#qunit-fixture').html(`
      <div id="one">ONE</div>
      <div id="two">TWO</div>
    `);
    super();
  }

  get applicationOptions() {
    return assign(super.applicationOptions, {
      rootElement: '#one'
    });
  }

  createSecondApplication(options, MyApplication=Application) {
    let myOptions = assign(this.applicationOptions, {
      rootElement: '#two'
    }, options);
    let secondApp = this.secondApp = MyApplication.create(myOptions);
    return secondApp;
  }

  teardown() {
    super.teardown();

    if (this.secondApp) {
      this.runTask(() => this.secondApp.destroy());
    }
  }

  [`@test initializers require proper 'name' and 'initialize' properties`]() {
    let MyApplication = Application.extend();

    expectAssertion(() => {
      MyApplication.instanceInitializer({ name: 'initializer' });
    });

    expectAssertion(() => {
      MyApplication.instanceInitializer({ initialize() {} });
    });

    this.runTask(() => this.createApplication({}, MyApplication));
  }

  [`@test initializers are passed an app instance`](assert) {
    let MyApplication = Application.extend();

    MyApplication.instanceInitializer({
      name: 'initializer',
      initialize(instance) {
        assert.ok(instance instanceof ApplicationInstance, 'initialize is passed an application instance');
      }
    });

    this.runTask(() => this.createApplication({}, MyApplication));
  }

  [`@test initializers can be registered in a specified order`](assert) {
    let order = [];
    let MyApplication = Application.extend();

    MyApplication.instanceInitializer({
      name: 'fourth',
      after: 'third',
      initialize(registry) {
        order.push('fourth');
      }
    });

    MyApplication.instanceInitializer({
      name: 'second',
      after: 'first',
      before: 'third',
      initialize(registry) {
        order.push('second');
      }
    });

    MyApplication.instanceInitializer({
      name: 'fifth',
      after: 'fourth',
      before: 'sixth',
      initialize(registry) {
        order.push('fifth');
      }
    });

    MyApplication.instanceInitializer({
      name: 'first',
      before: 'second',
      initialize(registry) {
        order.push('first');
      }
    });

    MyApplication.instanceInitializer({
      name: 'third',
      initialize(registry) {
        order.push('third');
      }
    });

    MyApplication.instanceInitializer({
      name: 'sixth',
      initialize(registry) {
        order.push('sixth');
      }
    });

    this.runTask(() => this.createApplication({}, MyApplication));

    assert.deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']);
  }

  [`@test initializers can be registered in a specified order as an array`](assert) {
    let order = [];
    let MyApplication = Application.extend();

    MyApplication.instanceInitializer({
      name: 'third',
      initialize(registry) {
        order.push('third');
      }
    });

    MyApplication.instanceInitializer({
      name: 'second',
      after: 'first',
      before: ['third', 'fourth'],
      initialize(registry) {
        order.push('second');
      }
    });

    MyApplication.instanceInitializer({
      name: 'fourth',
      after: ['second', 'third'],
      initialize(registry) {
        order.push('fourth');
      }
    });

    MyApplication.instanceInitializer({
      name: 'fifth',
      after: 'fourth',
      before: 'sixth',
      initialize(registry) {
        order.push('fifth');
      }
    });

    MyApplication.instanceInitializer({
      name: 'first',
      before: ['second'],
      initialize(registry) {
        order.push('first');
      }
    });

    MyApplication.instanceInitializer({
      name: 'sixth',
      initialize(registry) {
        order.push('sixth');
      }
    });

    this.runTask(() => this.createApplication({}, MyApplication));

    assert.deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']);
  }

  [`@test initializers can have multiple dependencies`](assert) {
    let order = [];
    let MyApplication = Application.extend();
    let a = {
      name: 'a',
      before: 'b',
      initialize(registry) {
        order.push('a');
      }
    };
    let b = {
      name: 'b',
      initialize(registry) {
        order.push('b');
      }
    };
    let c = {
      name: 'c',
      after: 'b',
      initialize(registry) {
        order.push('c');
      }
    };
    let afterB = {
      name: 'after b',
      after: 'b',
      initialize(registry) {
        order.push('after b');
      }
    };
    let afterC = {
      name: 'after c',
      after: 'c',
      initialize(registry) {
        order.push('after c');
      }
    };

    MyApplication.instanceInitializer(b);
    MyApplication.instanceInitializer(a);
    MyApplication.instanceInitializer(afterC);
    MyApplication.instanceInitializer(afterB);
    MyApplication.instanceInitializer(c);

    this.runTask(() => this.createApplication({}, MyApplication));

    assert.ok(order.indexOf(a.name) < order.indexOf(b.name), 'a < b');
    assert.ok(order.indexOf(b.name) < order.indexOf(c.name), 'b < c');
    assert.ok(order.indexOf(b.name) < order.indexOf(afterB.name), 'b < afterB');
    assert.ok(order.indexOf(c.name) < order.indexOf(afterC.name), 'c < afterC');
  }

  [`@test initializers set on Application subclasses should not be shared between apps`](assert) {
    let firstInitializerRunCount = 0;
    let secondInitializerRunCount = 0;
    let FirstApp = Application.extend();

    FirstApp.instanceInitializer({
      name: 'first',
      initialize(registry) {
        firstInitializerRunCount++;
      }
    });

    let SecondApp = Application.extend();
    SecondApp.instanceInitializer({
      name: 'second',
      initialize(registry) {
        secondInitializerRunCount++;
      }
    });

    this.runTask(() => this.createApplication({}, FirstApp));

    assert.equal(firstInitializerRunCount, 1, 'first initializer only was run');
    assert.equal(secondInitializerRunCount, 0, 'first initializer only was run');

    this.runTask(() => this.createSecondApplication({}, SecondApp));

    assert.equal(firstInitializerRunCount, 1, 'second initializer only was run');
    assert.equal(secondInitializerRunCount, 1, 'second initializer only was run');
  }

  [`@test initializers are concatenated`](assert) {
    let firstInitializerRunCount = 0;
    let secondInitializerRunCount = 0;
    let FirstApp = Application.extend();

    FirstApp.instanceInitializer({
      name: 'first',
      initialize(registry) {
        firstInitializerRunCount++;
      }
    });

    let SecondApp = FirstApp.extend();
    SecondApp.instanceInitializer({
      name: 'second',
      initialize(registry) {
        secondInitializerRunCount++;
      }
    });

    this.runTask(() => this.createApplication({}, FirstApp));

    equal(firstInitializerRunCount, 1, 'first initializer only was run when base class created');
    equal(secondInitializerRunCount, 0, 'first initializer only was run when base class created');

    firstInitializerRunCount = 0;
    this.runTask(() => this.createSecondApplication({}, SecondApp));

    equal(firstInitializerRunCount, 1, 'first initializer was run when subclass created');
    equal(secondInitializerRunCount, 1, 'second initializers was run when subclass created');
  }

  [`@test initializers are per-app`](assert) {
    assert.expect(2);

    let FirstApp = Application.extend();
    FirstApp.instanceInitializer({
      name: 'abc',
      initialize(app) {}
    });

    expectAssertion(function() {
      FirstApp.instanceInitializer({
        name: 'abc',
        initialize(app) {}
      });
    });

    this.runTask(() => this.createApplication({}, FirstApp));

    let SecondApp = Application.extend();
    SecondApp.instanceInitializer({
      name: 'abc',
      initialize(app) {}
    });

    this.runTask(() => this.createSecondApplication({}, SecondApp));

    assert.ok(true, 'Two apps can have initializers named the same.');
  }

  [`@test initializers are run before ready hook`](assert) {
    assert.expect(2);

    let MyApplication = Application.extend({
      ready() {
        assert.ok(true, 'ready is called');
        readyWasCalled = false;
      }
    });
    let readyWasCalled = false;

    MyApplication.instanceInitializer({
      name: 'initializer',
      initialize() {
        assert.ok(!readyWasCalled, 'ready is not yet called');
      }
    });

    this.runTask(() => this.createApplication({}, MyApplication));
  }

  [`@test initializers are executed in their own context`](assert) {
    assert.expect(1);

    let MyApplication = Application.extend();

    MyApplication.instanceInitializer({
      name: 'coolInitializer',
      myProperty: 'cool',
      initialize(registry, application) {
        assert.equal(this.myProperty, 'cool', 'should have access to its own context');
      }
    });

    this.runTask(() => this.createApplication({}, MyApplication));
  }

  [`@test initializers get an instance on app reset`](assert) {
    assert.expect(2);

    let MyApplication = Application.extend();

    MyApplication.instanceInitializer({
      name: 'giveMeAnInstance',
      initialize(instance) {
        assert.ok(!!instance, 'Initializer got an instance');
      }
    });

    this.runTask(() => this.createApplication({}, MyApplication));

    this.runTask(() => this.application.reset());
  }
});
Пример #15
0
moduleFor(
  'ember-metal/alias',
  class extends AbstractTestCase {
    beforeEach() {
      obj = { foo: { faz: 'FOO' } };
      count = 0;
    }

    afterEach() {
      obj = null;
    }

    ['@test should proxy get to alt key'](assert) {
      defineProperty(obj, 'bar', alias('foo.faz'));
      assert.equal(get(obj, 'bar'), 'FOO');
    }

    ['@test should proxy set to alt key'](assert) {
      defineProperty(obj, 'bar', alias('foo.faz'));
      set(obj, 'bar', 'BAR');
      assert.equal(get(obj, 'foo.faz'), 'BAR');
    }

    ['@test old dependent keys should not trigger property changes'](assert) {
      let obj1 = Object.create(null);
      defineProperty(obj1, 'foo', null, null);
      defineProperty(obj1, 'bar', alias('foo'));
      defineProperty(obj1, 'baz', alias('foo'));
      defineProperty(obj1, 'baz', alias('bar')); // redefine baz
      addObserver(obj1, 'baz', incrementCount);

      set(obj1, 'foo', 'FOO');
      assert.equal(count, 1);

      removeObserver(obj1, 'baz', incrementCount);

      set(obj1, 'foo', 'OOF');
      assert.equal(count, 1);
    }

    [`@test inheriting an observer of the alias from the prototype then
    redefining the alias on the instance to another property dependent on same key
    does not call the observer twice`](assert) {
      let obj1 = Object.create(null);

      meta(obj1).proto = obj1;

      defineProperty(obj1, 'foo', null, null);
      defineProperty(obj1, 'bar', alias('foo'));
      defineProperty(obj1, 'baz', alias('foo'));
      addObserver(obj1, 'baz', incrementCount);

      let obj2 = Object.create(obj1);
      defineProperty(obj2, 'baz', alias('bar')); // override baz

      set(obj2, 'foo', 'FOO');
      assert.equal(count, 1);

      removeObserver(obj2, 'baz', incrementCount);

      set(obj2, 'foo', 'OOF');
      assert.equal(count, 1);
    }

    ['@test an observer of the alias works if added after defining the alias'](assert) {
      defineProperty(obj, 'bar', alias('foo.faz'));
      addObserver(obj, 'bar', incrementCount);
      assert.ok(isWatching(obj, 'foo.faz'));
      set(obj, 'foo.faz', 'BAR');
      assert.equal(count, 1);
    }

    ['@test an observer of the alias works if added before defining the alias'](assert) {
      addObserver(obj, 'bar', incrementCount);
      defineProperty(obj, 'bar', alias('foo.faz'));
      assert.ok(isWatching(obj, 'foo.faz'));
      set(obj, 'foo.faz', 'BAR');
      assert.equal(count, 1);
    }

    ['@test object with alias is dirtied if interior object of alias is set after consumption'](
      assert
    ) {
      defineProperty(obj, 'bar', alias('foo.faz'));
      get(obj, 'bar');

      let tag = tagFor(obj);
      let tagValue = tag.value();
      set(obj, 'foo.faz', 'BAR');

      assert.ok(!tag.validate(tagValue), 'setting the aliased key should dirty the object');
    }

    ['@test setting alias on self should fail assertion']() {
      expectAssertion(
        () => defineProperty(obj, 'bar', alias('bar')),
        "Setting alias 'bar' on self"
      );
    }
  }
);
Пример #16
0
moduleFor(
  'Helpers test: {{unbound}}',
  class extends RenderingTestCase {
    ['@test should be able to output a property without binding']() {
      this.render(`<div id="first">{{unbound content.anUnboundString}}</div>`, {
        content: {
          anUnboundString: 'No spans here, son.',
        },
      });

      this.assertText('No spans here, son.');

      runTask(() => this.rerender());

      this.assertText('No spans here, son.');

      runTask(() => set(this.context, 'content.anUnboundString', 'HEY'));

      this.assertText('No spans here, son.');

      runTask(() =>
        set(this.context, 'content', {
          anUnboundString: 'No spans here, son.',
        })
      );

      this.assertText('No spans here, son.');
    }

    ['@test should be able to use unbound helper in #each helper']() {
      this.render(`<ul>{{#each items as |item|}}<li>{{unbound item}}</li>{{/each}}</ul>`, {
        items: emberA(['a', 'b', 'c', 1, 2, 3]),
      });

      this.assertText('abc123');

      runTask(() => this.rerender());

      this.assertText('abc123');
    }

    ['@test should be able to use unbound helper in #each helper (with objects)']() {
      this.render(`<ul>{{#each items as |item|}}<li>{{unbound item.wham}}</li>{{/each}}</ul>`, {
        items: emberA([{ wham: 'bam' }, { wham: 1 }]),
      });

      this.assertText('bam1');

      runTask(() => this.rerender());

      this.assertText('bam1');

      runTask(() => this.context.items.setEach('wham', 'HEY'));

      this.assertText('bam1');

      runTask(() => set(this.context, 'items', emberA([{ wham: 'bam' }, { wham: 1 }])));

      this.assertText('bam1');
    }

    ['@test it should assert unbound cannot be called with multiple arguments']() {
      let willThrow = () => {
        this.render(`{{unbound foo bar}}`, {
          foo: 'BORK',
          bar: 'BLOOP',
        });
      };

      expectAssertion(
        willThrow,
        /unbound helper cannot be called with multiple params or hash params/
      );
    }

    ['@test should render on attributes']() {
      this.render(`<a href="{{unbound model.foo}}"></a>`, {
        model: { foo: 'BORK' },
      });

      this.assertHTML('<a href="BORK"></a>');

      runTask(() => this.rerender());

      this.assertHTML('<a href="BORK"></a>');

      runTask(() => set(this.context, 'model.foo', 'OOF'));

      this.assertHTML('<a href="BORK"></a>');

      runTask(() => set(this.context, 'model', { foo: 'BORK' }));

      this.assertHTML('<a href="BORK"></a>');
    }

    ['@test should property escape unsafe hrefs']() {
      let unsafeUrls = emberA([
        {
          name: 'Bob',
          url: 'javascript:bob-is-cool',
        },
        {
          name: 'James',
          url: 'vbscript:james-is-cool',
        },
        {
          name: 'Richard',
          url: 'javascript:richard-is-cool',
        },
      ]);

      this.render(
        `<ul>{{#each people as |person|}}<li><a href="{{unbound person.url}}">{{person.name}}</a></li>{{/each}}</ul>`,
        {
          people: unsafeUrls,
        }
      );

      let escapedHtml = strip`
      <ul>
        <li>
          <a href="unsafe:javascript:bob-is-cool">Bob</a>
        </li>
        <li>
          <a href="unsafe:vbscript:james-is-cool">James</a>
        </li>
        <li>
          <a href="unsafe:javascript:richard-is-cool">Richard</a>
        </li>
      </ul>
    `;

      this.assertHTML(escapedHtml);

      runTask(() => this.rerender());

      this.assertHTML(escapedHtml);

      runTask(() => this.context.people.setEach('url', 'http://google.com'));

      this.assertHTML(escapedHtml);

      runTask(() => set(this.context, 'people', unsafeUrls));

      this.assertHTML(escapedHtml);
    }

    ['@skip helper form updates on parent re-render']() {
      this.render(`{{unbound foo}}`, {
        foo: 'BORK',
      });

      this.assertText('BORK');

      runTask(() => this.rerender());

      this.assertText('BORK');

      runTask(() => set(this.context, 'foo', 'OOF'));

      this.assertText('BORK');

      runTask(() => this.rerender());

      this.assertText('OOF');

      runTask(() => set(this.context, 'foo', ''));

      this.assertText('OOF');

      runTask(() => set(this.context, 'foo', 'BORK'));

      runTask(() => this.rerender());

      this.assertText('BORK');
    }

    // semantics here is not guaranteed
    ['@test sexpr form does not update no matter what']() {
      this.registerHelper('capitalize', args => args[0].toUpperCase());

      this.render(`{{capitalize (unbound foo)}}`, {
        foo: 'bork',
      });

      this.assertText('BORK');

      runTask(() => this.rerender());

      this.assertText('BORK');

      runTask(() => {
        set(this.context, 'foo', 'oof');
        this.rerender();
      });

      this.assertText('BORK');

      runTask(() => set(this.context, 'foo', 'blip'));

      this.assertText('BORK');

      runTask(() => {
        set(this.context, 'foo', 'bork');
        this.rerender();
      });

      this.assertText('BORK');
    }

    ['@test sexpr in helper form does not update on parent re-render']() {
      this.registerHelper('capitalize', params => params[0].toUpperCase());

      this.registerHelper('doublize', params => `${params[0]} ${params[0]}`);

      this.render(`{{capitalize (unbound (doublize foo))}}`, {
        foo: 'bork',
      });

      this.assertText('BORK BORK');

      runTask(() => this.rerender());

      this.assertText('BORK BORK');

      runTask(() => {
        set(this.context, 'foo', 'oof');
        this.rerender();
      });

      this.assertText('BORK BORK');

      runTask(() => set(this.context, 'foo', 'blip'));

      this.assertText('BORK BORK');

      runTask(() => {
        set(this.context, 'foo', 'bork');
        this.rerender();
      });

      this.assertText('BORK BORK');
    }

    ['@test should be able to render an unbound helper invocation']() {
      this.registerHelper('repeat', ([value], { count }) => {
        let a = [];
        while (a.length < count) {
          a.push(value);
        }
        return a.join('');
      });

      this.render(
        `{{unbound (repeat foo count=bar)}} {{repeat foo count=bar}} {{unbound (repeat foo count=2)}} {{repeat foo count=4}}`,
        {
          foo: 'X',
          bar: 5,
        }
      );

      this.assertText('XXXXX XXXXX XX XXXX');

      runTask(() => this.rerender());

      this.assertText('XXXXX XXXXX XX XXXX');

      runTask(() => set(this.context, 'bar', 1));

      this.assertText('XXXXX X XX XXXX');

      runTask(() => set(this.context, 'bar', 5));

      this.assertText('XXXXX XXXXX XX XXXX');
    }

    ['@test should be able to render an bound helper invocation mixed with static values']() {
      this.registerHelper('surround', ([prefix, value, suffix]) => `${prefix}-${value}-${suffix}`);

      this.render(
        strip`
      {{unbound (surround model.prefix model.value "bar")}} {{surround model.prefix model.value "bar"}} {{unbound (surround "bar" model.value model.suffix)}} {{surround "bar" model.value model.suffix}}`,
        {
          model: {
            prefix: 'before',
            value: 'core',
            suffix: 'after',
          },
        }
      );

      this.assertText('before-core-bar before-core-bar bar-core-after bar-core-after');

      runTask(() => this.rerender());

      this.assertText('before-core-bar before-core-bar bar-core-after bar-core-after');

      runTask(() => {
        setProperties(this.context.model, {
          prefix: 'beforeChanged',
          value: 'coreChanged',
          suffix: 'afterChanged',
        });
      });

      this.assertText(
        'before-core-bar beforeChanged-coreChanged-bar bar-core-after bar-coreChanged-afterChanged'
      );

      runTask(() => {
        set(this.context, 'model', {
          prefix: 'before',
          value: 'core',
          suffix: 'after',
        });
      });

      this.assertText('before-core-bar before-core-bar bar-core-after bar-core-after');
    }

    ['@test should be able to render unbound forms of multi-arg helpers']() {
      this.registerHelper('fauxconcat', params => params.join(''));

      this.render(
        `{{fauxconcat model.foo model.bar model.bing}} {{unbound (fauxconcat model.foo model.bar model.bing)}}`,
        {
          model: {
            foo: 'a',
            bar: 'b',
            bing: 'c',
          },
        }
      );

      this.assertText('abc abc');

      runTask(() => this.rerender());

      this.assertText('abc abc');

      runTask(() => set(this.context, 'model.bar', 'X'));

      this.assertText('aXc abc');

      runTask(() =>
        set(this.context, 'model', {
          foo: 'a',
          bar: 'b',
          bing: 'c',
        })
      );

      this.assertText('abc abc');
    }

    async ['@test should be able to render an unbound helper invocation for helpers with dependent keys']() {
      this.registerHelper('capitalizeName', {
        destroy() {
          this.removeObserver('value.firstName', this, this.recompute);
          this._super(...arguments);
        },

        compute([value]) {
          if (this.get('value')) {
            this.removeObserver('value.firstName', this, this.recompute);
          }
          this.set('value', value);
          this.addObserver('value.firstName', this, this.recompute);
          return value ? get(value, 'firstName').toUpperCase() : '';
        },
      });

      this.registerHelper('concatNames', {
        destroy() {
          this.teardown();
          this._super(...arguments);
        },
        teardown() {
          this.removeObserver('value.firstName', this, this.recompute);
          this.removeObserver('value.lastName', this, this.recompute);
        },
        compute([value]) {
          if (this.get('value')) {
            this.teardown();
          }
          this.set('value', value);
          this.addObserver('value.firstName', this, this.recompute);
          this.addObserver('value.lastName', this, this.recompute);
          return (value ? get(value, 'firstName') : '') + (value ? get(value, 'lastName') : '');
        },
      });

      this.render(
        `{{capitalizeName person}} {{unbound (capitalizeName person)}} {{concatNames person}} {{unbound (concatNames person)}}`,
        {
          person: {
            firstName: 'shooby',
            lastName: 'taylor',
          },
        }
      );

      this.assertText('SHOOBY SHOOBY shoobytaylor shoobytaylor');

      runTask(() => this.rerender());
      await runLoopSettled();

      this.assertText('SHOOBY SHOOBY shoobytaylor shoobytaylor');

      runTask(() => set(this.context, 'person.firstName', 'sally'));
      await runLoopSettled();

      this.assertText('SALLY SHOOBY sallytaylor shoobytaylor');

      runTask(() =>
        set(this.context, 'person', {
          firstName: 'shooby',
          lastName: 'taylor',
        })
      );
      await runLoopSettled();

      this.assertText('SHOOBY SHOOBY shoobytaylor shoobytaylor');
    }

    ['@test should be able to render an unbound helper invocation in #each helper']() {
      this.registerHelper('capitalize', params => params[0].toUpperCase());

      this.render(
        `{{#each people as |person|}}{{capitalize person.firstName}} {{unbound (capitalize person.firstName)}}{{/each}}`,
        {
          people: emberA([
            {
              firstName: 'shooby',
              lastName: 'taylor',
            },
            {
              firstName: 'cindy',
              lastName: 'taylor',
            },
          ]),
        }
      );

      this.assertText('SHOOBY SHOOBYCINDY CINDY');

      runTask(() => this.rerender());

      this.assertText('SHOOBY SHOOBYCINDY CINDY');

      runTask(() => this.context.people.setEach('firstName', 'chad'));

      this.assertText('CHAD SHOOBYCHAD CINDY');

      runTask(() =>
        set(
          this.context,
          'people',
          emberA([
            {
              firstName: 'shooby',
              lastName: 'taylor',
            },
            {
              firstName: 'cindy',
              lastName: 'taylor',
            },
          ])
        )
      );

      this.assertText('SHOOBY SHOOBYCINDY CINDY');
    }

    async ['@test should be able to render an unbound helper invocation with bound hash options']() {
      this.registerHelper('capitalizeName', {
        destroy() {
          this.removeObserver('value.firstName', this, this.recompute);
          this._super(...arguments);
        },

        compute([value]) {
          if (this.get('value')) {
            this.removeObserver('value.firstName', this, this.recompute);
          }
          this.set('value', value);
          this.addObserver('value.firstName', this, this.recompute);
          return value ? get(value, 'firstName').toUpperCase() : '';
        },
      });

      this.registerHelper('concatNames', {
        destroy() {
          this.teardown();
          this._super(...arguments);
        },
        teardown() {
          this.removeObserver('value.firstName', this, this.recompute);
          this.removeObserver('value.lastName', this, this.recompute);
        },
        compute([value]) {
          if (this.get('value')) {
            this.teardown();
          }
          this.set('value', value);
          this.addObserver('value.firstName', this, this.recompute);
          this.addObserver('value.lastName', this, this.recompute);
          return (value ? get(value, 'firstName') : '') + (value ? get(value, 'lastName') : '');
        },
      });

      this.render(
        `{{capitalizeName person}} {{unbound (capitalizeName person)}} {{concatNames person}} {{unbound (concatNames person)}}`,
        {
          person: {
            firstName: 'shooby',
            lastName: 'taylor',
          },
        }
      );
      await runLoopSettled();

      this.assertText('SHOOBY SHOOBY shoobytaylor shoobytaylor');

      runTask(() => this.rerender());
      await runLoopSettled();

      this.assertText('SHOOBY SHOOBY shoobytaylor shoobytaylor');

      runTask(() => set(this.context, 'person.firstName', 'sally'));
      await runLoopSettled();

      this.assertText('SALLY SHOOBY sallytaylor shoobytaylor');

      runTask(() =>
        set(this.context, 'person', {
          firstName: 'shooby',
          lastName: 'taylor',
        })
      );

      this.assertText('SHOOBY SHOOBY shoobytaylor shoobytaylor');
    }

    ['@test should be able to render bound form of a helper inside unbound form of same helper']() {
      this.render(
        strip`
      {{#if (unbound model.foo)}}
        {{#if model.bar}}true{{/if}}
        {{#unless model.bar}}false{{/unless}}
      {{/if}}
      {{#unless (unbound model.notfoo)}}
        {{#if model.bar}}true{{/if}}
        {{#unless model.bar}}false{{/unless}}
      {{/unless}}`,
        {
          model: {
            foo: true,
            notfoo: false,
            bar: true,
          },
        }
      );

      this.assertText('truetrue');

      runTask(() => this.rerender());

      this.assertText('truetrue');

      runTask(() => set(this.context, 'model.bar', false));

      this.assertText('falsefalse');

      runTask(() =>
        set(this.context, 'model', {
          foo: true,
          notfoo: false,
          bar: true,
        })
      );

      this.assertText('truetrue');
    }

    ['@test yielding unbound does not update']() {
      let fooBarInstance;
      let FooBarComponent = Component.extend({
        init() {
          this._super(...arguments);
          fooBarInstance = this;
        },
        model: { foo: 'bork' },
      });

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template: `{{yield (unbound model.foo)}}`,
      });

      this.render(`{{#foo-bar as |value|}}{{value}}{{/foo-bar}}`);

      this.assertText('bork');

      runTask(() => this.rerender());

      this.assertText('bork');

      runTask(() => set(fooBarInstance, 'model.foo', 'oof'));

      this.assertText('bork');

      runTask(() => set(fooBarInstance, 'model', { foo: 'bork' }));

      this.assertText('bork');
    }

    ['@test yielding unbound hash does not update']() {
      let fooBarInstance;
      let FooBarComponent = Component.extend({
        init() {
          this._super(...arguments);
          fooBarInstance = this;
        },
        model: { foo: 'bork' },
      });

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template: `{{yield (unbound (hash foo=model.foo))}}`,
      });

      this.render(`{{#foo-bar as |value|}}{{value.foo}}{{/foo-bar}}`);

      this.assertText('bork');

      runTask(() => this.rerender());

      this.assertText('bork');

      runTask(() => set(fooBarInstance, 'model.foo', 'oof'));

      this.assertText('bork');

      runTask(() => set(fooBarInstance, 'model', { foo: 'bork' }));

      this.assertText('bork');
    }
  }
);
Пример #17
0
moduleFor(
  'Libraries registry',
  class extends AbstractTestCase {
    beforeEach() {
      libs = new Libraries();
      registry = libs._registry;
    }

    afterEach() {
      libs = null;
      registry = null;

      setDebugFunction('warn', originalWarn);
    }

    ['@test core libraries come before other libraries'](assert) {
      assert.expect(2);

      libs.register('my-lib', '2.0.0a');
      libs.registerCoreLibrary('DS', '1.0.0-beta.2');

      assert.equal(registry[0].name, 'DS');
      assert.equal(registry[1].name, 'my-lib');
    }

    ['@test only the first registration of a library is stored'](assert) {
      assert.expect(3);

      // overwrite warn to supress the double registration warning (see https://github.com/emberjs/ember.js/issues/16391)
      setDebugFunction('warn', noop);
      libs.register('magic', 1.23);
      libs.register('magic', 2.23);

      assert.equal(registry[0].name, 'magic');
      assert.equal(registry[0].version, 1.23);
      assert.equal(registry.length, 1);
    }

    ['@test isRegistered returns correct value'](assert) {
      if (EMBER_LIBRARIES_ISREGISTERED) {
        assert.expect(3);

        assert.equal(libs.isRegistered('magic'), false);

        libs.register('magic', 1.23);
        assert.equal(libs.isRegistered('magic'), true);

        libs.deRegister('magic');
        assert.equal(libs.isRegistered('magic'), false);
      } else {
        assert.expect(0);
      }
    }

    ['@test attempting to register a library that is already registered warns you'](assert) {
      if (EmberDev && EmberDev.runningProdBuild) {
        assert.ok(true, 'Logging does not occur in production builds');
        return;
      }

      assert.expect(1);

      libs.register('magic', 1.23);

      setDebugFunction('warn', function(msg, test) {
        if (!test) {
          assert.equal(msg, 'Library "magic" is already registered with Ember.');
        }
      });

      // Should warn us
      libs.register('magic', 2.23);
    }

    ['@test libraries can be de-registered'](assert) {
      assert.expect(2);

      libs.register('lib1', '1.0.0b');
      libs.register('lib2', '1.0.0b');
      libs.register('lib3', '1.0.0b');

      libs.deRegister('lib1');
      libs.deRegister('lib3');

      assert.equal(registry[0].name, 'lib2');
      assert.equal(registry.length, 1);
    }
  }
);
Пример #18
0
moduleFor(
  'EmberObject computed property',
  class extends AbstractTestCase {
    ['@test computed property on instance'](assert) {
      let MyClass = EmberObject.extend({
        foo: computed(function() {
          return 'FOO';
        }),
      });

      testWithDefault(assert, 'FOO', MyClass.create(), 'foo');
    }

    ['@test computed property on subclass'](assert) {
      let MyClass = EmberObject.extend({
        foo: computed(function() {
          return 'FOO';
        }),
      });

      let Subclass = MyClass.extend({
        foo: computed(function() {
          return 'BAR';
        }),
      });

      testWithDefault(assert, 'BAR', Subclass.create(), 'foo');
    }

    ['@test replacing computed property with regular val'](assert) {
      let MyClass = EmberObject.extend({
        foo: computed(function() {
          return 'FOO';
        }),
      });

      let Subclass = MyClass.extend({
        foo: 'BAR',
      });

      testWithDefault(assert, 'BAR', Subclass.create(), 'foo');
    }

    ['@test complex depndent keys'](assert) {
      let MyClass = EmberObject.extend({
        init() {
          this._super(...arguments);
          set(this, 'bar', { baz: 'BIFF' });
        },

        count: 0,

        foo: computed('bar.baz', function() {
          set(this, 'count', get(this, 'count') + 1);
          return get(get(this, 'bar'), 'baz') + ' ' + get(this, 'count');
        }),
      });

      let Subclass = MyClass.extend({
        count: 20,
      });

      let obj1 = MyClass.create();
      let obj2 = Subclass.create();

      testWithDefault(assert, 'BIFF 1', obj1, 'foo');
      testWithDefault(assert, 'BIFF 21', obj2, 'foo');

      set(get(obj1, 'bar'), 'baz', 'BLARG');

      testWithDefault(assert, 'BLARG 2', obj1, 'foo');
      testWithDefault(assert, 'BIFF 21', obj2, 'foo');

      set(get(obj2, 'bar'), 'baz', 'BOOM');

      testWithDefault(assert, 'BLARG 2', obj1, 'foo');
      testWithDefault(assert, 'BOOM 22', obj2, 'foo');
    }

    ['@test complex dependent keys changing complex dependent keys'](assert) {
      let MyClass = EmberObject.extend({
        init() {
          this._super(...arguments);
          set(this, 'bar', { baz: 'BIFF' });
        },

        count: 0,

        foo: computed('bar.baz', function() {
          set(this, 'count', get(this, 'count') + 1);
          return get(get(this, 'bar'), 'baz') + ' ' + get(this, 'count');
        }),
      });

      let Subclass = MyClass.extend({
        init() {
          this._super(...arguments);
          set(this, 'bar2', { baz: 'BIFF2' });
        },

        count: 0,

        foo: computed('bar2.baz', function() {
          set(this, 'count', get(this, 'count') + 1);
          return get(get(this, 'bar2'), 'baz') + ' ' + get(this, 'count');
        }),
      });

      let obj2 = Subclass.create();

      testWithDefault(assert, 'BIFF2 1', obj2, 'foo');

      set(get(obj2, 'bar'), 'baz', 'BLARG');
      testWithDefault(assert, 'BIFF2 1', obj2, 'foo'); // should not invalidate property

      set(get(obj2, 'bar2'), 'baz', 'BLARG');
      testWithDefault(assert, 'BLARG 2', obj2, 'foo'); // should not invalidate property
    }

    ['@test can retrieve metadata for a computed property'](assert) {
      let MyClass = EmberObject.extend({
        computedProperty: computed(function() {}).meta({ key: 'keyValue' }),
      });

      assert.equal(
        get(MyClass.metaForProperty('computedProperty'), 'key'),
        'keyValue',
        'metadata saved on the computed property can be retrieved'
      );

      let ClassWithNoMetadata = EmberObject.extend({
        computedProperty: computed(function() {}),

        staticProperty: 12,
      });

      assert.equal(
        typeof ClassWithNoMetadata.metaForProperty('computedProperty'),
        'object',
        'returns empty hash if no metadata has been saved'
      );

      expectAssertion(function() {
        ClassWithNoMetadata.metaForProperty('nonexistentProperty');
      }, "metaForProperty() could not find a computed property with key 'nonexistentProperty'.");

      expectAssertion(function() {
        ClassWithNoMetadata.metaForProperty('staticProperty');
      }, "metaForProperty() could not find a computed property with key 'staticProperty'.");
    }

    ['@test overriding a computed property with null removes it from eachComputedProperty iteration'](
      assert
    ) {
      let MyClass = EmberObject.extend({
        foo: computed(function() {}),

        fooDidChange: observer('foo', function() {}),

        bar: computed(function() {}),
      });

      let SubClass = MyClass.extend({
        foo: null,
      });

      let list = [];

      SubClass.eachComputedProperty(name => list.push(name));

      assert.deepEqual(
        list.sort(),
        ['bar'],
        'overridding with null removes from eachComputedProperty listing'
      );
    }

    ['@test can iterate over a list of computed properties for a class'](assert) {
      let MyClass = EmberObject.extend({
        foo: computed(function() {}),

        fooDidChange: observer('foo', function() {}),

        bar: computed(function() {}),

        qux: alias('foo'),
      });

      let SubClass = MyClass.extend({
        baz: computed(function() {}),
      });

      SubClass.reopen({
        bat: computed(function() {}).meta({ iAmBat: true }),
      });

      let list = [];

      MyClass.eachComputedProperty(function(name) {
        list.push(name);
      });

      assert.deepEqual(
        list.sort(),
        ['bar', 'foo', 'qux'],
        'watched and unwatched computed properties are iterated'
      );

      list = [];

      SubClass.eachComputedProperty(function(name, meta) {
        list.push(name);

        if (name === 'bat') {
          assert.deepEqual(meta, { iAmBat: true });
        } else {
          assert.deepEqual(meta, {});
        }
      });

      assert.deepEqual(
        list.sort(),
        ['bar', 'bat', 'baz', 'foo', 'qux'],
        'all inherited properties are included'
      );
    }

    ['@test list of properties updates when an additional property is added (such cache busting)'](
      assert
    ) {
      let MyClass = EmberObject.extend({
        foo: computed(K),

        fooDidChange: observer('foo', function() {}),

        bar: computed(K),
      });

      let list = [];

      MyClass.eachComputedProperty(function(name) {
        list.push(name);
      });

      assert.deepEqual(list.sort(), ['bar', 'foo'].sort(), 'expected two computed properties');

      MyClass.reopen({
        baz: computed(K),
      });

      MyClass.create(); // force apply mixins

      list = [];

      MyClass.eachComputedProperty(function(name) {
        list.push(name);
      });

      assert.deepEqual(
        list.sort(),
        ['bar', 'foo', 'baz'].sort(),
        'expected three computed properties'
      );

      defineProperty(MyClass.prototype, 'qux', computed(K));

      list = [];

      MyClass.eachComputedProperty(function(name) {
        list.push(name);
      });

      assert.deepEqual(
        list.sort(),
        ['bar', 'foo', 'baz', 'qux'].sort(),
        'expected four computed properties'
      );
    }

    ['@test Calling _super in call outside the immediate function of a CP getter works'](assert) {
      function macro(callback) {
        return computed(function() {
          return callback.call(this);
        });
      }

      let MyClass = EmberObject.extend({
        foo: computed(function() {
          return 'FOO';
        }),
      });

      let SubClass = MyClass.extend({
        foo: macro(function() {
          return this._super();
        }),
      });

      assert.ok(get(SubClass.create(), 'foo'), 'FOO', 'super value is fetched');
    }

    ['@test Calling _super in apply outside the immediate function of a CP getter works'](assert) {
      function macro(callback) {
        return computed(function() {
          return callback.apply(this);
        });
      }

      let MyClass = EmberObject.extend({
        foo: computed(function() {
          return 'FOO';
        }),
      });

      let SubClass = MyClass.extend({
        foo: macro(function() {
          return this._super();
        }),
      });

      assert.ok(get(SubClass.create(), 'foo'), 'FOO', 'super value is fetched');
    }

    ['@test observing computed.reads prop and overriding it in create() works'](assert) {
      let Obj = EmberObject.extend({
        name: reads('model.name'),
        nameDidChange: observer('name', function() {}),
      });

      let obj1 = Obj.create({ name: '1' });
      let obj2 = Obj.create({ name: '2' });

      assert.equal(obj1.get('name'), '1');
      assert.equal(obj2.get('name'), '2');
    }

    ['@test can declare dependent keys with .property()'](assert) {
      let Obj;

      expectDeprecation(() => {
        Obj = EmberObject.extend({
          foo: computed(function() {
            return this.bar;
          }).property('bar'),
        });
      }, /Setting dependency keys using the `.property\(\)` modifier has been deprecated/);

      let obj = Obj.create({ bar: 1 });

      assert.equal(obj.get('foo'), 1);

      obj.set('bar', 2);

      assert.equal(obj.get('foo'), 2);
    }
  }
);
moduleFor(
  'Application Lifecycle - Component Registration',
  class extends ApplicationTestCase {
    // This is necessary for this.application.instanceInitializer to not leak between tests
    createApplication(options) {
      return super.createApplication(options, Application.extend());
    }

    ['@test The helper becomes the body of the component']() {
      this.addTemplate('components/expand-it', '<p>hello {{yield}}</p>');
      this.addTemplate('application', 'Hello world {{#expand-it}}world{{/expand-it}}');

      return this.visit('/').then(() => {
        this.assertText('Hello world hello world');
        this.assertComponentElement(this.element.firstElementChild, {
          tagName: 'div',
          content: '<p>hello world</p>',
        });
      });
    }

    ['@test The helper becomes the body of the component (ENV._TEMPLATE_ONLY_GLIMMER_COMPONENTS = true;)']() {
      ENV._TEMPLATE_ONLY_GLIMMER_COMPONENTS = true;
      this.addTemplate('components/expand-it', '<p>hello {{yield}}</p>');
      this.addTemplate('application', 'Hello world {{#expand-it}}world{{/expand-it}}');

      return this.visit('/').then(() => {
        this.assertInnerHTML('Hello world <p>hello world</p>');
        ENV._TEMPLATE_ONLY_GLIMMER_COMPONENTS = false;
      });
    }

    ['@test If a component is registered, it is used'](assert) {
      this.addTemplate('components/expand-it', '<p>hello {{yield}}</p>');
      this.addTemplate('application', `Hello world {{#expand-it}}world{{/expand-it}}`);

      this.application.instanceInitializer({
        name: 'expand-it-component',
        initialize(applicationInstance) {
          applicationInstance.register(
            'component:expand-it',
            Component.extend({
              classNames: 'testing123',
            })
          );
        },
      });

      return this.visit('/').then(() => {
        let text = this.$('div.testing123')
          .text()
          .trim();
        assert.equal(text, 'hello world', 'The component is composed correctly');
      });
    }

    ['@test Late-registered components can be rendered with custom `layout` property'](assert) {
      this.addTemplate('application', `<div id='wrapper'>there goes {{my-hero}}</div>`);

      this.application.instanceInitializer({
        name: 'my-hero-component',
        initialize(applicationInstance) {
          applicationInstance.register(
            'component:my-hero',
            Component.extend({
              classNames: 'testing123',
              layout: compile('watch him as he GOES'),
            })
          );
        },
      });

      return this.visit('/').then(() => {
        let text = this.$('#wrapper')
          .text()
          .trim();
        assert.equal(
          text,
          'there goes watch him as he GOES',
          'The component is composed correctly'
        );
      });
    }

    ['@test Late-registered components can be rendered with template registered on the container'](
      assert
    ) {
      this.addTemplate(
        'application',
        `<div id='wrapper'>hello world {{sally-rutherford}}-{{#sally-rutherford}}!!!{{/sally-rutherford}}</div>`
      );

      this.application.instanceInitializer({
        name: 'sally-rutherford-component-template',
        initialize(applicationInstance) {
          applicationInstance.register(
            'template:components/sally-rutherford',
            compile('funkytowny{{yield}}')
          );
        },
      });
      this.application.instanceInitializer({
        name: 'sally-rutherford-component',
        initialize(applicationInstance) {
          applicationInstance.register('component:sally-rutherford', Component);
        },
      });

      return this.visit('/').then(() => {
        let text = this.$('#wrapper')
          .text()
          .trim();
        assert.equal(
          text,
          'hello world funkytowny-funkytowny!!!',
          'The component is composed correctly'
        );
      });
    }

    ['@test Late-registered components can be rendered with ONLY the template registered on the container'](
      assert
    ) {
      this.addTemplate(
        'application',
        `<div id='wrapper'>hello world {{borf-snorlax}}-{{#borf-snorlax}}!!!{{/borf-snorlax}}</div>`
      );

      this.application.instanceInitializer({
        name: 'borf-snorlax-component-template',
        initialize(applicationInstance) {
          applicationInstance.register(
            'template:components/borf-snorlax',
            compile('goodfreakingTIMES{{yield}}')
          );
        },
      });

      return this.visit('/').then(() => {
        let text = this.$('#wrapper')
          .text()
          .trim();
        assert.equal(
          text,
          'hello world goodfreakingTIMES-goodfreakingTIMES!!!',
          'The component is composed correctly'
        );
      });
    }

    ['@test Assigning layoutName to a component should setup the template as a layout'](assert) {
      assert.expect(1);

      this.addTemplate(
        'application',
        `<div id='wrapper'>{{#my-component}}{{text}}{{/my-component}}</div>`
      );
      this.addTemplate('foo-bar-baz', '{{text}}-{{yield}}');

      this.application.instanceInitializer({
        name: 'application-controller',
        initialize(applicationInstance) {
          applicationInstance.register(
            'controller:application',
            Controller.extend({
              text: 'outer',
            })
          );
        },
      });
      this.application.instanceInitializer({
        name: 'my-component-component',
        initialize(applicationInstance) {
          applicationInstance.register(
            'component:my-component',
            Component.extend({
              text: 'inner',
              layoutName: 'foo-bar-baz',
            })
          );
        },
      });

      return this.visit('/').then(() => {
        let text = this.$('#wrapper')
          .text()
          .trim();
        assert.equal(text, 'inner-outer', 'The component is composed correctly');
      });
    }

    ['@test Assigning layoutName and layout to a component should use the `layout` value'](assert) {
      assert.expect(1);

      this.addTemplate(
        'application',
        `<div id='wrapper'>{{#my-component}}{{text}}{{/my-component}}</div>`
      );
      this.addTemplate('foo-bar-baz', 'No way!');

      this.application.instanceInitializer({
        name: 'application-controller-layout',
        initialize(applicationInstance) {
          applicationInstance.register(
            'controller:application',
            Controller.extend({
              text: 'outer',
            })
          );
        },
      });
      this.application.instanceInitializer({
        name: 'my-component-component-layout',
        initialize(applicationInstance) {
          applicationInstance.register(
            'component:my-component',
            Component.extend({
              text: 'inner',
              layoutName: 'foo-bar-baz',
              layout: compile('{{text}}-{{yield}}'),
            })
          );
        },
      });

      return this.visit('/').then(() => {
        let text = this.$('#wrapper')
          .text()
          .trim();
        assert.equal(text, 'inner-outer', 'The component is composed correctly');
      });
    }

    ['@test Using name of component that does not exist']() {
      this.addTemplate('application', `<div id='wrapper'>{{#no-good}} {{/no-good}}</div>`);

      // TODO: Use the async form of expectAssertion here when it is available
      expectAssertion(() => {
        this.visit('/');
      }, /.* named "no-good" .*/);

      return this.runLoopSettled();
    }
  }
);
import { Controller } from 'ember-runtime';
import { QueryParamTestCase, moduleFor } from 'internal-test-helpers';

moduleFor('Query Params - paramless link-to', class extends QueryParamTestCase {
  testParamlessLinks(assert, routeName) {
    assert.expect(1);

    this.addTemplate(routeName, '{{link-to \'index\' \'index\' id=\'index-link\'}}');

    this.add(`controller:${routeName}`, Controller.extend({
      queryParams: ['foo'],
      foo: 'wat'
    }));

    return this.visit('/?foo=YEAH').then(() => {
      assert.equal(document.getElementById('index-link').getAttribute('href'), '/?foo=YEAH');
    });
  }

  ['@test param-less links in an app booted with query params in the URL don\'t reset the query params: application'](assert) {
    return this.testParamlessLinks(assert, 'application');
  }

  ['@test param-less links in an app booted with query params in the URL don\'t reset the query params: index'](assert) {
    return this.testParamlessLinks(assert, 'index');
  }
});
Пример #21
0
moduleFor(
  'syntax refinements',
  class extends RenderingTestCase {
    ['@test block params should not be refined']() {
      this.registerHelper('foo', () => 'bar helper');

      this.render(
        strip`
      {{#with var as |foo|}}
        {{foo}}
      {{/with}}

      ---

      {{#with var as |outlet|}}
        {{outlet}}
      {{/with}}

      ---

      {{#with var as |mount|}}
        {{mount}}
      {{/with}}

      ---

      {{#with var as |component|}}
        {{component}}
      {{/with}}

      ---

      {{#with var as |input|}}
        {{input}}
      {{/with}}

      ---

      {{#with var as |-with-dynamic-vars|}}
        {{-with-dynamic-vars}}
      {{/with}}

      ---

      {{#with var as |-in-element|}}
        {{-in-element}}
      {{/with}}`,
        { var: 'var' }
      );

      this.assertText('var---var---var---var---var---var---var');

      runTask(() => set(this.context, 'var', 'RARRR!!!'));

      this.assertText('RARRR!!!---RARRR!!!---RARRR!!!---RARRR!!!---RARRR!!!---RARRR!!!---RARRR!!!');

      runTask(() => set(this.context, 'var', 'var'));

      this.assertText('var---var---var---var---var---var---var');
    }
  }
);
Пример #22
0
classes.forEach((TestClass, testCaseIndex) => {
  moduleFor(
    TestClass.module('@ember/-internals/metal/descriptor'),
    class extends AbstractTestCase {
      ['@test defining a configurable property'](assert) {
        let factory = new TestClass(assert);

        factory.install('foo', descriptor({ configurable: true, value: 'bar' }), assert);

        let obj = factory.finalize();

        assert.equal(obj.foo, 'bar');

        let source = factory.source();

        delete source.foo;

        assert.strictEqual(obj.foo, undefined);

        Object.defineProperty(source, 'foo', { configurable: true, value: 'baz' });

        assert.equal(obj.foo, 'baz');
      }

      ['@test defining a non-configurable property'](assert) {
        let factory = new TestClass(assert);
        factory.install('foo', descriptor({ configurable: false, value: 'bar' }), assert);

        let obj = factory.finalize();

        assert.equal(obj.foo, 'bar');

        let source = factory.source();

        if (isEdge && testCaseIndex === 0) {
          // https://github.com/emberjs/ember.js/pull/16741#issuecomment-420963181
          assert.equal(delete source.foo, false);
        } else {
          assert.throws(() => delete source.foo, TypeError);
        }

        assert.throws(
          () =>
            Object.defineProperty(source, 'foo', {
              configurable: true,
              value: 'baz',
            }),
          TypeError
        );

        assert.equal(obj.foo, 'bar');
      }

      ['@test defining an enumerable property'](assert) {
        let factory = new TestClass(assert);
        factory.install('foo', descriptor({ enumerable: true, value: 'bar' }), assert);

        let obj = factory.finalize();

        assert.equal(obj.foo, 'bar');

        let source = factory.source();

        assert.ok(Object.keys(source).indexOf('foo') !== -1);
      }

      ['@test defining a non-enumerable property'](assert) {
        let factory = new TestClass(assert);
        factory.install('foo', descriptor({ enumerable: false, value: 'bar' }), assert);

        let obj = factory.finalize();

        assert.equal(obj.foo, 'bar');

        let source = factory.source();

        assert.ok(Object.keys(source).indexOf('foo') === -1);
      }

      ['@test defining a writable property'](assert) {
        let factory = new TestClass(assert);
        factory.install('foo', descriptor({ writable: true, value: 'bar' }), assert);

        let obj = factory.finalize();

        assert.equal(obj.foo, 'bar');

        let source = factory.source();

        source.foo = 'baz';

        assert.equal(obj.foo, 'baz');

        obj.foo = 'bat';

        assert.equal(obj.foo, 'bat');
      }

      ['@test defining a non-writable property'](assert) {
        let factory = new TestClass(assert);
        factory.install('foo', descriptor({ writable: false, value: 'bar' }), assert);

        let obj = factory.finalize();

        assert.equal(obj.foo, 'bar');

        let source = factory.source();

        assert.throws(() => (source.foo = 'baz'), TypeError);
        assert.throws(() => (obj.foo = 'baz'), TypeError);

        assert.equal(obj.foo, 'bar');
      }

      ['@test defining a getter'](assert) {
        let factory = new TestClass(assert);
        factory.install(
          'foo',
          descriptor({
            get: function() {
              return this.__foo__;
            },
          }),
          assert
        );

        factory.set('__foo__', 'bar');

        let obj = factory.finalize();

        assert.equal(obj.foo, 'bar');

        obj.__foo__ = 'baz';

        assert.equal(obj.foo, 'baz');
      }

      ['@test defining a setter'](assert) {
        let factory = new TestClass(assert);
        factory.install(
          'foo',
          descriptor({
            set: function(value) {
              this.__foo__ = value;
            },
          }),
          assert
        );

        factory.set('__foo__', 'bar');

        let obj = factory.finalize();

        assert.equal(obj.__foo__, 'bar');

        obj.foo = 'baz';

        assert.equal(obj.__foo__, 'baz');
      }

      ['@test combining multiple setter and getters'](assert) {
        let factory = new TestClass(assert);
        factory.install(
          'foo',
          descriptor({
            get: function() {
              return this.__foo__;
            },

            set: function(value) {
              this.__foo__ = value;
            },
          }),
          assert
        );

        factory.set('__foo__', 'foo');

        factory.install(
          'bar',
          descriptor({
            get: function() {
              return this.__bar__;
            },

            set: function(value) {
              this.__bar__ = value;
            },
          }),
          assert
        );

        factory.set('__bar__', 'bar');

        factory.install(
          'fooBar',
          descriptor({
            get: function() {
              return this.foo + '-' + this.bar;
            },
          }),
          assert
        );

        let obj = factory.finalize();

        assert.equal(obj.fooBar, 'foo-bar');

        obj.foo = 'FOO';

        assert.equal(obj.fooBar, 'FOO-bar');

        obj.__bar__ = 'BAR';

        assert.equal(obj.fooBar, 'FOO-BAR');

        assert.throws(() => (obj.fooBar = 'foobar'), TypeError);

        assert.equal(obj.fooBar, 'FOO-BAR');
      }
    }
  );
});
Пример #23
0
import { OWNER } from 'ember-utils';
import { Container, Registry } from 'container';
import ContainerProxy from '../../lib/mixins/container_proxy';
import EmberObject from '../../lib/system/object';
import { moduleFor, AbstractTestCase } from 'internal-test-helpers';

moduleFor(
  'ember-runtime/mixins/container_proxy',
  class extends AbstractTestCase {
    beforeEach() {
      this.Owner = EmberObject.extend(ContainerProxy);
      this.instance = this.Owner.create();

      let registry = new Registry();

      this.instance.__container__ = new Container(registry, {
        owner: this.instance,
      });
    }

    ['@test provides ownerInjection helper method'](assert) {
      let result = this.instance.ownerInjection();

      assert.equal(result[OWNER], this.instance, 'returns an object with the OWNER symbol');
    }
  }
);
Пример #24
0
  moduleFor(
    'Component Tracked Properties',
    class extends RenderingTestCase {
      '@test tracked properties that are uninitialized do not throw an error'() {
        let CountComponent = Component.extend({
          count: tracked(),

          increment() {
            if (!this.count) {
              this.count = 0;
            }
            this.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.count}}</button>',
        });

        this.render('<Counter />');

        this.assertText('');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test tracked properties rerender when updated'() {
        let CountComponent = Component.extend({
          count: tracked({ value: 0 }),

          increment() {
            this.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.count}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test tracked properties rerender when updated outside of a runloop'(assert) {
        let done = assert.async();

        let CountComponent = Component.extend({
          count: tracked({ value: 0 }),

          increment() {
            setTimeout(() => {
              this.count++;
            }, 100);
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.count}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        // intentionally outside of a runTask
        this.$('button').click();

        setTimeout(() => {
          this.assertText('1');
          done();
        }, 200);
      }

      '@test nested tracked properties rerender when updated'() {
        let Counter = EmberObject.extend({
          count: tracked({ value: 0 }),
        });

        let CountComponent = Component.extend({
          counter: Counter.create(),

          increment() {
            this.counter.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.counter.count}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test getters update when dependent properties are invalidated'() {
        let CountComponent = Component.extend({
          count: tracked({ value: 0 }),

          countAlias: descriptor({
            get() {
              return this.count;
            },
          }),

          increment() {
            this.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.countAlias}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test nested getters update when dependent properties are invalidated'() {
        let Counter = EmberObject.extend({
          count: tracked({ value: 0 }),

          countAlias: descriptor({
            get() {
              return this.count;
            },
          }),
        });

        let CountComponent = Component.extend({
          counter: Counter.create(),

          increment() {
            this.counter.count++;
          },
        });

        this.registerComponent('counter', {
          ComponentClass: CountComponent,
          template: '<button {{action this.increment}}>{{this.counter.countAlias}}</button>',
        });

        this.render('<Counter />');

        this.assertText('0');

        runTask(() => this.$('button').click());

        this.assertText('1');
      }

      '@test tracked object passed down through components updates correctly'(assert) {
        let Person = EmberObject.extend({
          first: tracked({ value: 'Rob' }),
          last: tracked({ value: 'Jackson' }),

          full: descriptor({
            get() {
              return `${this.first} ${this.last}`;
            },
          }),
        });

        let ParentComponent = Component.extend({
          person: Person.create(),
        });

        let ChildComponent = Component.extend({
          updatePerson() {
            this.person.first = 'Kris';
            this.person.last = 'Selden';
          },
        });

        this.registerComponent('parent', {
          ComponentClass: ParentComponent,
          template: strip`
            <div id="parent">{{this.person.full}}</div>
            <Child @person={{this.person}}/>
          `,
        });

        this.registerComponent('child', {
          ComponentClass: ChildComponent,
          template: strip`
            <div id="child">{{this.person.full}}</div>
            <button onclick={{action this.updatePerson}}></button>
          `,
        });

        this.render('<Parent />');

        assert.equal(this.$('#parent').text(), 'Rob Jackson');
        assert.equal(this.$('#child').text(), 'Rob Jackson');

        runTask(() => this.$('button').click());

        assert.equal(this.$('#parent').text(), 'Kris Selden');
        assert.equal(this.$('#child').text(), 'Kris Selden');
      }

      '@test yielded getters update correctly'() {
        let PersonComponent = Component.extend({
          first: tracked({ value: 'Rob' }),
          last: tracked({ value: 'Jackson' }),

          full: descriptor({
            get() {
              return `${this.first} ${this.last}`;
            },
          }),

          updatePerson() {
            this.first = 'Kris';
            this.last = 'Selden';
          },
        });

        this.registerComponent('person', {
          ComponentClass: PersonComponent,
          template: strip`
            {{yield this.full (action this.updatePerson)}}
          `,
        });

        this.render(strip`
          <Person as |name update|>
            <button onclick={{update}}>
              {{name}}
            </button>
          </Person>
        `);

        this.assertText('Rob Jackson');

        runTask(() => this.$('button').click());

        this.assertText('Kris Selden');
      }

      '@test yielded nested getters update correctly'() {
        let Person = EmberObject.extend({
          first: tracked({ value: 'Rob' }),
          last: tracked({ value: 'Jackson' }),

          full: descriptor({
            get() {
              return `${this.first} ${this.last}`;
            },
          }),
        });

        let PersonComponent = Component.extend({
          person: Person.create(),

          updatePerson() {
            this.person.first = 'Kris';
            this.person.last = 'Selden';
          },
        });

        this.registerComponent('person', {
          ComponentClass: PersonComponent,
          template: strip`
            {{yield this.person (action this.updatePerson)}}
          `,
        });

        this.render(strip`
          <Person as |p update|>
            <button onclick={{update}}>
              {{p.full}}
            </button>
          </Person>
        `);

        this.assertText('Rob Jackson');

        runTask(() => this.$('button').click());

        this.assertText('Kris Selden');
      }
    }
  );
Пример #25
0
moduleFor('Application Lifecycle - Component Registration', class extends AutobootApplicationTestCase {

  ['@test The helper becomes the body of the component'](assert) {
    this.runTask(() => {
      this.createApplication();

      this.addTemplate('components/expand-it', '<p>hello {{yield}}</p>');
      this.addTemplate('application', 'Hello world {{#expand-it}}world{{/expand-it}}');
    });

    let text = this.$('div.ember-view > div.ember-view').text().trim();
    assert.equal(text, 'hello world', 'The component is composed correctly');
  }

  ['@test If a component is registered, it is used'](assert) {
    this.runTask(() => {
      this.createApplication();

      this.addTemplate('components/expand-it', '<p>hello {{yield}}</p>');
      this.addTemplate('application', `Hello world {{#expand-it}}world{{/expand-it}}`);

      this.applicationInstance.register('component:expand-it', Component.extend({
        classNames: 'testing123'
      }));
    });

    let text = this.$('div.testing123').text().trim();
    assert.equal(text, 'hello world', 'The component is composed correctly');
  }

  ['@test Late-registered components can be rendered with custom `layout` property'](assert) {
    this.runTask(() => {
      this.createApplication();

      this.addTemplate('application', `<div id='wrapper'>there goes {{my-hero}}</div>`);

      this.applicationInstance.register('component:my-hero', Component.extend({
        classNames: 'testing123',
        layout: this.compile('watch him as he GOES')
      }));
    });

    let text = this.$('#wrapper').text().trim();
    assert.equal(text, 'there goes watch him as he GOES', 'The component is composed correctly');
  }

  ['@test Late-registered components can be rendered with template registered on the container'](assert) {
    this.runTask(() => {
      this.createApplication();

      this.addTemplate('application', `<div id='wrapper'>hello world {{sally-rutherford}}-{{#sally-rutherford}}!!!{{/sally-rutherford}}</div>`);

      this.applicationInstance.register('template:components/sally-rutherford', this.compile('funkytowny{{yield}}'));
      this.applicationInstance.register('component:sally-rutherford', Component);
    });

    let text = this.$('#wrapper').text().trim();
    assert.equal(text, 'hello world funkytowny-funkytowny!!!', 'The component is composed correctly');
  }

  ['@test Late-registered components can be rendered with ONLY the template registered on the container'](assert) {
    this.runTask(() => {
      this.createApplication();

      this.addTemplate('application', `<div id='wrapper'>hello world {{borf-snorlax}}-{{#borf-snorlax}}!!!{{/borf-snorlax}}</div>`);

      this.applicationInstance.register('template:components/borf-snorlax', this.compile('goodfreakingTIMES{{yield}}'));
    });

    let text = this.$('#wrapper').text().trim();
    assert.equal(text, 'hello world goodfreakingTIMES-goodfreakingTIMES!!!', 'The component is composed correctly');
  }

  ['@test Assigning layoutName to a component should setup the template as a layout'](assert) {
    assert.expect(1);

    this.runTask(() => {
      this.createApplication();

      this.addTemplate('application', `<div id='wrapper'>{{#my-component}}{{text}}{{/my-component}}</div>`);
      this.addTemplate('foo-bar-baz', '{{text}}-{{yield}}');

      this.applicationInstance.register('controller:application', Controller.extend({
        text: 'outer'
      }));
      this.applicationInstance.register('component:my-component', Component.extend({
        text: 'inner',
        layoutName: 'foo-bar-baz'
      }));
    });

    let text = this.$('#wrapper').text().trim();
    assert.equal(text, 'inner-outer', 'The component is composed correctly');
  }

  ['@test Assigning layoutName and layout to a component should use the `layout` value'](assert) {
    assert.expect(1);

    this.runTask(() => {
      this.createApplication();

      this.addTemplate('application', `<div id='wrapper'>{{#my-component}}{{text}}{{/my-component}}</div>`);
      this.addTemplate('foo-bar-baz', 'No way!');

      this.applicationInstance.register('controller:application', Controller.extend({
        text: 'outer'
      }));
      this.applicationInstance.register('component:my-component', Component.extend({
        text: 'inner',
        layoutName: 'foo-bar-baz',
        layout: this.compile('{{text}}-{{yield}}')
      }));
    });

    let text = this.$('#wrapper').text().trim();
    assert.equal(text, 'inner-outer', 'The component is composed correctly');
  }

  /*
   * When an exception is thrown during the initial rendering phase, the
   * `visit` promise is not resolved or rejected. This means the `applicationInstance`
   * is never torn down and tests running after this one will fail.
   *
   * It is ugly, but since this test intentionally causes an initial render
   * error, it requires globals mode to access the `applicationInstance`
   * for teardown after test completion.
   *
   * Application "globals mode" is trigged by `autoboot: true`. It doesn't
   * have anything to do with the resolver.
   *
   * We should be able to fix this by having the application eagerly stash a
   * copy of each application instance it creates. When the application is
   * destroyed, it can also destroy the instances (this is how the globals
   * mode avoid the problem).
   *
   * See: https://github.com/emberjs/ember.js/issues/15327
   */
  ['@test Using name of component that does not exist']() {
    expectAssertion(() => {
      this.runTask(() => {
        this.createApplication();

        this.addTemplate('application', `<div id='wrapper'>{{#no-good}} {{/no-good}}</div>`);
      });
    }, /.* named "no-good" .*/);
  }
});
Пример #26
0
moduleFor(
  'Template scoping examples',
  class extends ApplicationTestCase {
    ['@test Actions inside an outlet go to the associated controller'](assert) {
      this.add(
        'controller:index',
        Controller.extend({
          actions: {
            componentAction() {
              assert.ok(true, 'controller received the action');
            },
          },
        })
      );

      this.addComponent('component-with-action', {
        ComponentClass: Component.extend({
          classNames: ['component-with-action'],
          click() {
            this.sendAction();
          },
        }),
      });

      this.addTemplate('index', '{{component-with-action action="componentAction"}}');

      return this.visit('/').then(() => {
        this.runTask(() => this.$('.component-with-action').click());
      });
    }
  }
);
Пример #27
0
moduleFor(
  'EmberStringUtils.loc',
  class extends AbstractTestCase {
    beforeEach() {
      oldString = getStrings();
      setStrings({
        '_Hello World': 'Bonjour le monde',
        '_Hello %@': 'Bonjour %@',
        '_Hello %@ %@': 'Bonjour %@ %@',
        '_Hello %@# %@#': 'Bonjour %@2 %@1',
      });
    }

    afterEach() {
      setStrings(oldString);
    }

    ['@test String.prototype.loc is not available without EXTEND_PROTOTYPES'](assert) {
      if (!ENV.EXTEND_PROTOTYPES.String) {
        assert.ok('undefined' === typeof String.prototype.loc, 'String.prototype helper disabled');
      } else {
        assert.expect(0);
      }
    }

    ['@test String loc tests'](assert) {
      test(
        assert,
        '_Hello World',
        [],
        'Bonjour le monde',
        `loc('_Hello World') => 'Bonjour le monde'`
      );
      test(
        assert,
        '_Hello %@ %@',
        ['John', 'Doe'],
        'Bonjour John Doe',
        `loc('_Hello %@ %@', ['John', 'Doe']) => 'Bonjour John Doe'`
      );
      test(
        assert,
        '_Hello %@# %@#',
        ['John', 'Doe'],
        'Bonjour Doe John',
        `loc('_Hello %@# %@#', ['John', 'Doe']) => 'Bonjour Doe John'`
      );
      test(
        assert,
        '_Not In Strings',
        [],
        '_Not In Strings',
        `loc('_Not In Strings') => '_Not In Strings'`
      );
    }

    ['@test works with argument form'](assert) {
      assert.equal(loc('_Hello %@', 'John'), 'Bonjour John');
      assert.equal(loc('_Hello %@ %@', ['John'], 'Doe'), 'Bonjour [John] Doe');
    }
  }
);
Пример #28
0
moduleFor(
  'Application Dependency Injection - normalize',
  class extends TestCase {
    constructor() {
      super();

      application = run(Application, 'create');
      registry = application.__registry__;
    }

    teardown() {
      run(application, 'destroy');
    }

    ['@test normalization'](assert) {
      assert.ok(registry.normalize, 'registry#normalize is present');

      assert.equal(registry.normalize('foo:bar'), 'foo:bar');

      assert.equal(registry.normalize('controller:posts'), 'controller:posts');
      assert.equal(registry.normalize('controller:posts_index'), 'controller:postsIndex');
      assert.equal(registry.normalize('controller:posts.index'), 'controller:postsIndex');
      assert.equal(registry.normalize('controller:posts-index'), 'controller:postsIndex');
      assert.equal(registry.normalize('controller:posts.post.index'), 'controller:postsPostIndex');
      assert.equal(registry.normalize('controller:posts_post.index'), 'controller:postsPostIndex');
      assert.equal(registry.normalize('controller:posts.post_index'), 'controller:postsPostIndex');
      assert.equal(registry.normalize('controller:posts.post-index'), 'controller:postsPostIndex');
      assert.equal(registry.normalize('controller:postsIndex'), 'controller:postsIndex');
      assert.equal(registry.normalize('controller:blogPosts.index'), 'controller:blogPostsIndex');
      assert.equal(registry.normalize('controller:blog/posts.index'), 'controller:blog/postsIndex');
      assert.equal(registry.normalize('controller:blog/posts-index'), 'controller:blog/postsIndex');
      assert.equal(
        registry.normalize('controller:blog/posts.post.index'),
        'controller:blog/postsPostIndex'
      );
      assert.equal(
        registry.normalize('controller:blog/posts_post.index'),
        'controller:blog/postsPostIndex'
      );
      assert.equal(
        registry.normalize('controller:blog/posts_post-index'),
        'controller:blog/postsPostIndex'
      );

      assert.equal(registry.normalize('template:blog/posts_index'), 'template:blog/posts_index');
    }

    ['@test normalization is indempotent'](assert) {
      let examples = [
        'controller:posts',
        'controller:posts.post.index',
        'controller:blog/posts.post_index',
        'template:foo_bar',
      ];

      examples.forEach(example => {
        assert.equal(registry.normalize(registry.normalize(example)), registry.normalize(example));
      });
    }
  }
);
import { compile } from '../../index';
import { moduleFor, AbstractTestCase } from 'internal-test-helpers';

moduleFor(
  'ember-template-compiler: transforms dot component invocation',
  class extends AbstractTestCase {
    ['@test Does not throw a compiler error for path components'](assert) {
      assert.expect(0);

      [
        '{{this.modal open}}',
        '{{this.modal isOpen=true}}',
        '{{#this.modal}}Woot{{/this.modal}}',
        '{{c.modal open}}',
        '{{c.modal isOpen=true}}',
        '{{#c.modal}}Woot{{/c.modal}}',
        '{{#my-component as |c|}}{{c.a name="Chad"}}{{/my-component}}',
        '{{#my-component as |c|}}{{c.a "Chad"}}{{/my-component}}',
        '{{#my-component as |c|}}{{#c.a}}{{/c.a}}{{/my-component}}',
        '<input disabled={{true}}>', // GH#15740
        '<td colspan={{3}}></td>', // GH#15217
      ].forEach((layout, i) => {
        compile(layout, { moduleName: `example-${i}` });
      });
    }
  }
);
Пример #30
0
import { compileOptions } from '../../index';
import { defaultPlugins } from '../../index';
import { moduleFor, AbstractTestCase } from 'internal-test-helpers';

moduleFor('ember-template-compiler: default compile options', class extends AbstractTestCase {
  ['@test default options are a new copy'](assert) {
    assert.notEqual(compileOptions(), compileOptions());
  }

  ['@test has default AST plugins'](assert) {
    assert.expect(defaultPlugins.length);

    let plugins = compileOptions().plugins.ast;

    for (let i = 0; i < defaultPlugins.length; i++) {
      let plugin = defaultPlugins[i];
      assert.ok(plugins.indexOf(plugin) > -1, `includes ${plugin}`);
    }
  }
});