profile
viewpoint

artursmirnov/ember-cli-openlayers-builder 3

Openlayers builder for Ember CLI

artursmirnov/react-deploy-cli 2

CLI tool to deploy React App to S3 and CloudFront

artursmirnov/broccoli-scss-lint 1

Broccoli plugin for scss-lint

artursmirnov/build-pushes 1

a trial repo for PR and Pushes config

artursmirnov/hola 1

test project :)

artursmirnov/ansiparse 0

Parse ANSI color codes

artursmirnov/apiarius 0

Automatic API documentation generator

artursmirnov/aws-ec2-vpn 0

Set up a VPN server on an AWS EC2 Instance via a self contained Cloud Formation template.

artursmirnov/broccoli-scss-linter 0

Broccoli plugin for validate .scss files

delete branch travis-ci/travis-web

delete branch : negative_balance_fix

delete time in 7 days

push eventtravis-ci/travis-web

Piotr Milcarz

commit sha cfd721fa586a7146010f977c927264431b713c16

Negative balance fix (#2546) * missing beforeModel added * tests adopted to new allowance rules * Update tests/acceptance/repo/settings-test.js Co-authored-by: Artur Smirnov <reg@artursmirnov.com> Co-authored-by: Artur Smirnov <reg@artursmirnov.com>

view details

push time in 7 days

PullRequestReviewEvent

Pull request review commenttravis-ci/travis-web

Negative balance fix

 module('Acceptance | repo settings', function (hooks) {       login: 'org-login',     }); +    this.server.create('allowance', {subscription_type: 1});+    this.server.create('allowance', {subscription_type: 1});

Is it a duplication? If it's intended, you could use

    this.server.createList('allowance', 2, {subscription_type: 1});

Does it make sense?

piotrmilcarz

comment created time in 7 days

PullRequestReviewEvent
PullRequestReviewEvent

push eventtravis-ci/travis-web

Pavel Dotsulenko

commit sha d93a48a38ae231554d0a761ea8fb0660607686ee

Fix for addon purchase process (#2545)

view details

push time in 7 days

delete branch travis-ci/travis-web

delete branch : pd-addon-purchase-fix

delete time in 7 days

PR merged travis-ci/travis-web

Fix for addon purchase process

if user tries to change plan after canceling addon purchase, previously selected addon is displayed on the order summary page

+5 -1

0 comment

2 changed files

pavel-d

pr closed time in 7 days

PullRequestReviewEvent

push eventtravis-ci/travis-web

Piotr Milcarz

commit sha 2380b3691275df5965b13a588a3a1414cf55bef3

Corner cases handling (#2541) * WIP on new Plan change page * Further work on v2 plan change page * More work in progress on v2 plan change page * Fix tests * WIP billing and payment info steps to plan change * Continued work on v2 Plan change page * Plan change page WIP for users with no billing * WIP on fixing tests for v2 plan change * WIP for v2 Plan page * Add credit usage bar to the plan page * Add Buy Addons button * Work on API connection for v2 Plan change * Some tests and work on plan summary and change pages * Fix tests * Fix review comments part 1 * Fix review comments part 2 * Adjust plan docs link * Use adapter for fetching v2-plan-config * Fix more review comments * Show plan selector with GH subscription * Billing warning messages + Switch to free modal * fix * whitespace fix * some code was refactored * fix * fix * debug * test is moving to another pr * improvement * Remove logging Co-authored-by: Andrii Mysko <usandrewmisko@gmail.com> Co-authored-by: AndriiMysko <69478317+AndriiMysko@users.noreply.github.com> Co-authored-by: Artur Smirnov <reg@artursmirnov.com>

view details

push time in 7 days

delete branch travis-ci/travis-web

delete branch : corner_cases_handling

delete time in 7 days

push eventtravis-ci/travis-web

Artur Smirnov

commit sha f112df7b0f093266f044927d99989ca05492aa3a

Remove logging

view details

push time in 7 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

+<Modal+  @isVisible={{@isOpen}}+  @onClose={{fn @onClose}}+  @closeButton={{true}}+>+  <div data-test-switchtofree-subscription-modal="true" class="flex flex--v-center flex--center flex--col switchtofree-subscription-modal">+    <h2>Looks like you are changing your plan</h2>+    <p class="switchtofree-subscription-modal__message">+      To keep improving our product and how it works for our users,<br />we’d like to hear your feedback on why you’re changing your plan+    </p>+    <h5>Reason for changing your plan</h5>+    <div class="flex switchtofree-subscription-modal__reason-options">+      {{#each this.switchToFreeReasons as |reason|}}+        <div data-test-switchtofree-reason-option='true' onClick={{action 'selectSwitchToFreeReason' reason}} class='{{if (eq selectedSwitchToFreeReason reason.name) 'selected'}}'>+          <p>{{reason.name}}</p>+        </div>+      {{/each}}+    </div>+    <div class="full-width">+      <TravisForm+        @onSubmit={{perform this.switchToFreeSubscription}}+        as |form|+      >+        <div class="form-elem switchtofree-information">+          <form.field+            @label=""+            @onChange={{action (mut this.switchToFreeReasonDetails)}}+            as |field|+          >+            <field.textarea rows="4" placeholder="Could you share more information with us?">{{this.switchToFreeReasonDetails}}</field.textarea>+          </form.field>+        </div>+        {{log }}

Could you please remove this one?

piotrmilcarz

comment created time in 7 days

PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commenttravis-ci/travis-web

Corner cases handling

 export default Model.extend({     return yield this.api.post(`/v2_subscription/${this.id}/pay`);   }).drop(), +  switchToFreeSubscription: task(function* (data) {+    yield this.api.post(`/v2_subscription/${this.id}/changetofree`, {+      data+    });+    yield this.accounts.fetchV2Subscriptions.perform();+  }).drop(),

This feedback is not implemented. @piotrmilcarz it should be easy to implement, could you please take a look?

piotrmilcarz

comment created time in 7 days

PullRequestReviewEvent

push eventtravis-ci/travis-web

Piotr Milcarz

commit sha d97c5db707d9ec33ba2a1c49dcb540003210c996

Owner allowance for repository context - refactored (#2544) * Allowance for repository refactored * text fix * text fix * better wording * Update app/models/repo.js Co-authored-by: Artur Smirnov <reg@artursmirnov.com> * improvements * tests fix * tests fixed * fix * fix * fix * missing newline at end of file Co-authored-by: Artur Smirnov <reg@artursmirnov.com>

view details

push time in 7 days

delete branch travis-ci/travis-web

delete branch : owner_allowance_refactor

delete time in 7 days

PullRequestReviewEvent

Pull request review commenttravis-ci/travis-web

Owner allowance for repository context - refactored

 module('Acceptance | repo/trigger build', function (hooks) {   setupMirage(hooks);    hooks.beforeEach(function () {+    this.server.create('allowance', {+      id: 1,

This too

piotrmilcarz

comment created time in 7 days

PullRequestReviewEvent

Pull request review commenttravis-ci/travis-web

Owner allowance for repository context - refactored

 module('Acceptance | repo allowance', function (hooks) {     const currentUser = this.server.create('user', {login: 'user-login'});     signInUser(currentUser); +    this.server.create('user', {login: 'user-login2'});+    this.server.create('user', {login: 'user-login3'});+     const repoPrivate = this.server.create('repository', {       name: 'repository-private',       slug: 'user-login/repository-private',       'private': true,       active: true,       owner: {         login: 'user-login',-        allowance: {-          subscription_type: 2,-          private_repos: false,-          public_repos: true,-          concurrency_limit: 1-        }+        id: 1       },       vcs_name: 'repository-private',       owner_name: 'user-login'     }); +    this.server.create('allowance', {+      id: 1,+      subscription_type: 2,+      public_repos: false,+      private_repos: false,+      concurrency_limit: 777+    }),++    this.server.create('allowance', {+      id: 2,+      subscription_type: 2,+      public_repos: true,+      private_repos: true,+      concurrency_limit: 666+    }),++    this.server.create('allowance', {+      id: 3,+      subscription_type: 2,+      public_repos: true,+      private_repos: false,+      concurrency_limit: 2+    }),

This comment isn't fixed. Although, let's keep it as is for now. It seems to be working with generated ids too, so this is basically just a confusing thing. I'd appreciate if you could clean this up sometime later, don't want to hold this PR because of it.

piotrmilcarz

comment created time in 7 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commenttravis-ci/travis-web

Owner allowance for repository context - refactored

 const Repo = VcsEntity.extend({     const allowance = this.allowance;     const isPrivate = this.private; -    if (allowance && allowance.subscription_type === 1)+    if (allowance && allowance.subscriptionType === 1)       return true;-    if (!allowance)+    if (!allowance && !this.repoOwnerAllowance) {+      return true; // pending allowance call

Yes, it definitely is :)

piotrmilcarz

comment created time in 7 days

push eventtravis-ci/travis-web

Pavel Dotsulenko

commit sha e6d6c2d21ef093b34cf5d6088fbec1b54e1626c2

Separate addon purchase (#2542) * Separate addons purchase * Make eslint happy * Add tests * Fix eslint errors * Fix specs * Apply suggestions from code review Co-authored-by: Artur Smirnov <reg@artursmirnov.com> * Apply review fixes * Apply review fixes * Apply review fixes * Fix button misalignment Co-authored-by: Artur Smirnov <reg@artursmirnov.com>

view details

push time in 7 days

delete branch travis-ci/travis-web

delete branch : pd-separate-addon-purchase

delete time in 7 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commenttravis-ci/travis-web

Owner allowance for repository context - refactored

 module('Acceptance | repo allowance', function (hooks) {     const currentUser = this.server.create('user', {login: 'user-login'});     signInUser(currentUser); +    this.server.create('user', {login: 'user-login2'});+    this.server.create('user', {login: 'user-login3'});+     const repoPrivate = this.server.create('repository', {       name: 'repository-private',       slug: 'user-login/repository-private',       'private': true,       active: true,       owner: {         login: 'user-login',-        allowance: {-          subscription_type: 2,-          private_repos: false,-          public_repos: true,-          concurrency_limit: 1-        }+        id: 1       },       vcs_name: 'repository-private',       owner_name: 'user-login'     }); +    this.server.create('allowance', {+      id: 1,+      subscription_type: 2,+      public_repos: false,+      private_repos: false,+      concurrency_limit: 777+    }),++    this.server.create('allowance', {+      id: 2,+      subscription_type: 2,+      public_repos: true,+      private_repos: true,+      concurrency_limit: 666+    }),++    this.server.create('allowance', {+      id: 3,+      subscription_type: 2,+      public_repos: true,+      private_repos: false,+      concurrency_limit: 2+    }),

You shouldn't specify ids manually, Mirage's factories will generate them automatically and take care of uniqueness.

piotrmilcarz

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Owner allowance for repository context - refactored

 export default function () {     }   }); +  this.get('/owner/:provider/:login/allowance', function (schema, request) {+    let owner = schema.users.where({ login: request.params.login }).models[0];

This won't work for organizations. Is it expected?

piotrmilcarz

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Owner allowance for repository context - refactored

 module('Acceptance | repo/view migrated', function (hooks) {      await signInUser(user); +    this.server.create('allowance', {+      id: 1,

And here

piotrmilcarz

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Owner allowance for repository context - refactored

 module('Acceptance | repo/trigger build', function (hooks) {   setupMirage(hooks);    hooks.beforeEach(function () {+    this.server.create('allowance', {+      id: 1,

Same here with ids

piotrmilcarz

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Owner allowance for repository context - refactored

 const Repo = VcsEntity.extend({     const allowance = this.allowance;     const isPrivate = this.private; -    if (allowance && allowance.subscription_type === 1)+    if (allowance && allowance.subscriptionType === 1)       return true;-    if (!allowance)+    if (!allowance && !this.repoOwnerAllowance) {+      return true; // pending allowance call

It seems to me that this should return false. Until we know exactly that user is allowed to build, we shouldn't allow them to do so. Let's say the allowance request hung for a while and user had a chance to trigger a build (for example, via Restart) - they will likely get an error from API. If it's necessary to have this data already fetched at this point, you can trigger the task run in corresponding route - beforeModel, model and afterModel hooks are blocking view rendering until their returned promises are resolved. Does it make sense to you?

piotrmilcarz

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Owner allowance for repository context - refactored

 const Repo = VcsEntity.extend({     return hasPermissions && (!isMigrated || isFailed);   }), -  allowance: reads('owner.allowance'),+  allowance: computed('owner.allowance', 'repoOwnerAllowance', function () {+    if (this.owner.allowance)+      return this.owner.allowance;+    if (this.repoOwnerAllowance)+      return this.repoOwnerAllowance;+    this.fetchRepoOwnerAllowance.perform();+  }),++  repoOwnerAllowance: reads('fetchRepoOwnerAllowance.lastSuccessful.value'),++  fetchRepoOwnerAllowance: task(function* () {+    const allowance = this.store.peekRecord('allowance', this.owner.id);+    if (allowance)+      return allowance;+    yield this.store.queryRecord('allowance', { login: this.owner.login, provider: this.provider });

The yielded value needs to be returned in order to get set as task's value:

    return yield this.store.queryRecord('allowance', { login: this.owner.login, provider: this.provider });

Currently, the fetchRepoOwnerAllowance.lastSuccessful.value will be undefined after fetch.

piotrmilcarz

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Owner allowance for repository context - refactored

 const Repo = VcsEntity.extend({     return hasPermissions && (!isMigrated || isFailed);   }), -  allowance: reads('owner.allowance'),+  allowance: computed('owner.allowance', 'repoOwnerAllowance', function () {+    if (this.owner.allowance)+      return this.owner.allowance;+    if (this.repoOwnerAllowance)+      return this.repoOwnerAllowance;+    this.fetchRepoOwnerAllowance.perform();+  }),++  repoOwnerAllowance: reads('fetchRepoOwnerAllowance.lastSuccessful.value'),++  fetchRepoOwnerAllowance: task(function* () {+    const allowance = this.store.peekRecord('allowance', this.owner.id);

peekRecord takes a model name and record id as arguments. And id assumed to be of the same type as the model name. Therefore, peekRecord with this implementation will return an allowance record, which id is equal to this.owner.id, which seems wrong to me. owner and allowance have different ids, aren't they? According to the current data structures, I'd suggest the following:

    const allowance = this.store.peekAll('allowance ').findBy('owner.id', this.owner.id);
piotrmilcarz

comment created time in 8 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commenttravis-ci/travis-web

Separate addon purchase

+import { module, test } from 'qunit';+import { setupRenderingTest } from 'ember-qunit';+import profilePage from 'travis/tests/pages/profile';+import { render } from '@ember/test-helpers';+import hbs from 'htmlbars-inline-precompile';++module('Integration | Component | selected-addon', function (hooks) {+  setupRenderingTest(hooks);++  test('it renders', async function (assert) {++    this['actions'] = {+      goToFirstStep: () => { }+    };++    const addonConfig = {+      id: 'credits_500k',+      name: '500 000 credits (50k Linux build minutes)',+      price: 30000,+      quantity: 500000,+      type: 'credit_private'+    };++    this.selectedAddon = addonConfig;+    this.set('selectedAddon', addonConfig);

I don't think this duplication is needed:

    this.set('selectedAddon', addonConfig);
pavel-d

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Separate addon purchase

+<section class='billing-select-plan'>+  <TravisForm @onSubmit={{perform this.save}} as |form|>+    <h3 data-test-billing-buy-addon-info-title>+      Buy Addons+    </h3>+    <p class='billing-plans__addons_box--desc color-asphalt-grey' data-test-billing-buy-addon-info-description>+      Choose addons you want to add to your plan+    </p>+    <div class='plan-selector'>+      <div class='billing-plans flex flex--wrap' data-test-billing-addon-choices>+        {{#each this.displayedStandaloneAddons as |addon|}}+          <div+            data-test-addon-box='true'+            class='billing-plans__addons_box pointer {{if (eq addon.id this.selectedAddon.id) 'highlight-plan'}}'+            onclick={{action (mut this.selectedAddon) addon}}+          >+            <p class='billing-plans__addons_box--price' data-test-selected-addon-price>+              {{format-currency addon.price floor="true"}}+            </p>+            <p class='billing-plans__addons_box--name color-asphalt-grey' data-test-selected-addon-name>+              Additional credits+            </p>+            <p class='billing-plans__addons_box--desc color-asphalt-grey' data-test-selected-addon-desc>+              {{addon.quantity}} credits+            </p>+          </div>+        {{/each}}+      </div>+    </div>+    <div>+      <span class='billing-plans__addons_box--desc'>Total:</span>+      <span class='billing-plans__addons_box--price'>+        {{format-currency this.totalPrice floor="true"}}+      </span>+    </div>+    <div class="flex flex--wrap {{if this.account.hasSubscriptionPermissions 'billing-subscription__buttons'}}">+      {{#if this.isLoading}}

I didn't find any declaration of isLoading on the component class. Is it expected? According to the logic I can assume that it could likely be this.save.isRunning.

pavel-d

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Separate addon purchase

     margin-bottom: 1em;     margin-top: 1em; +    &__addons_box {+      background-color: #fff;+      border-radius: 5px;+      padding: 6px 13px;+      border: 1px solid $pebble-grey;+      margin-bottom: 10px;+      width: 208px;+      height: 114px;+      flex-basis: auto;+      margin-right: 5px;++      &--desc {+        color: $asphalt-grey;+        font-weight: 600;+        font-size: 0.8em;+        margin: 5px 0 12px 0;+        color: #9d9d9d;+        text-transform: uppercase;++        span {+          color: $cement-grey;+          font-size: .8em;+          font-style: italic;+        }+      }++      &--help {+        &:hover {+          border-bottom: 1px solid;+          cursor: pointer;+        }+      }++      &--price {+        margin: 0 0 10px 0;+        font-weight: 300;+        font-size: 1.7em;+        text-align: left;+        color: $oxide-blue;++        span {+          color: $cement-grey;+          font-size: .5em;+        }+      }++      &--name {+        margin: 0px 0 10px 0;+        color: #666666;

This is asphalt-grey color.

pavel-d

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Separate addon purchase

+<h2>HUI</h2>

Is this a leftover?

pavel-d

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Separate addon purchase

     margin-bottom: 1em;     margin-top: 1em; +    &__addons_box {+      background-color: #fff;+      border-radius: 5px;+      padding: 6px 13px;+      border: 1px solid $pebble-grey;+      margin-bottom: 10px;+      width: 208px;+      height: 114px;+      flex-basis: auto;+      margin-right: 5px;++      &--desc {+        color: $asphalt-grey;+        font-weight: 600;+        font-size: 0.8em;+        margin: 5px 0 12px 0;+        color: #9d9d9d;

This color property overwrites the one above. Is it on purpose? Also it'd be better to reference colors via variables (cement-grey in this case), because they are more meaningful and this allows to better understand what color stands behind it.

pavel-d

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Separate addon purchase

       />     {{/if}}   </section>-  {{#if (and this.invoices (not this.showPlansSelector))}}+  {{#if (and this.invoices (and (not this.showPlansSelector) (not this.showAddonsSelector)))}}

Would it make sense to extract this complex condition to a computed property on component? It becomes quite heavy, both in terms of understanding and performance (template helpers are getting recalculated on each render, whereas computed properties are cached and update only when their dependencies update). Something like {{#if this.showInvoices}}. Although, this is completely optional suggestion and is totally up to you, as this way it perfectly works as well.

pavel-d

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Separate addon purchase

+import Component from '@ember/component';+import { inject as service } from '@ember/service';+import { task } from 'ember-concurrency';+import { computed } from '@ember/object';+import { later } from '@ember/runloop';+import { not, reads } from '@ember/object/computed';++export default Component.extend({+  accounts: service(),+  store: service(),++  account: null,+  title: null,+  selectedAddon: null,+  availableStandaloneAddons: computed('account.availableStandaloneAddons', function () {+    return this.account.availableStandaloneAddons.sortBy('price');+  }),+  isButtonDisabled: not('selectedAddon'),+  displayedStandaloneAddons: reads('availableStandaloneAddons'),+  totalPrice: computed('selectedAddon', function () {

This property should also track price, otherwise it won't update when price changes:

  totalPrice: computed('selectedAddon.price', function () {

I understand that addon setting unlikely will change in application runtime, but according to my experience, it's usually better to overlook such things and implement them properly to avoid nasty bugs in future.

pavel-d

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Separate addon purchase

+import Component from '@ember/component';+import { inject as service } from '@ember/service';+import { task } from 'ember-concurrency';+import { computed } from '@ember/object';+import { later } from '@ember/runloop';+import { not, reads } from '@ember/object/computed';++export default Component.extend({+  accounts: service(),+  store: service(),++  account: null,+  title: null,+  selectedAddon: null,+  availableStandaloneAddons: computed('account.availableStandaloneAddons', function () {

This property should also track price property of each addon, otherwise it won't update if any of them change:

  availableStandaloneAddons: computed('account.availableStandaloneAddons.[].price', function () {
pavel-d

comment created time in 8 days

Pull request review commenttravis-ci/travis-web

Separate addon purchase

+import Component from '@ember/component';+import { computed } from '@ember/object';++export default Component.extend({+  total: computed('selectedAddon', function () {+    return this.selectedAddon.price;+  })

This could be just

  total: reads('selectedAddon.price')
pavel-d

comment created time in 8 days

PullRequestReviewEvent
PullRequestReviewEvent

delete branch travis-ci/travis-web

delete branch : request-config-staging

delete time in 9 days

PR closed travis-ci/travis-web

Request config staging do_not_merge

For testing on staging

+2770 -620

0 comment

99 changed files

artursmirnov

pr closed time in 9 days

PR opened travis-ci/travis-web

Request config staging do_not_merge

For testing on staging

+2770 -620

0 comment

99 changed files

pr created time in 9 days

create barnchtravis-ci/travis-web

branch : request-config-staging

created branch time in 9 days

push eventtravis-ci/travis-web

Artur Smirnov

commit sha 9d4013c78b5ddacb909c24da278cebc7c34cfd9f

Update VAT field display according to new requirements (#2532) * Update VAT field display according to new requirements * Fix tests * Add an attribute to pass local registration flag to API * Fix address form for new subscription, add new VAT rules to edit billing address form * Fix form errors

view details

Artur Smirnov

commit sha 92350efae0f4773e728a6bc51ae2b8719c2d8b04

Merge branch 'master' into sf-request-config

view details

push time in 9 days

PullRequestReviewEvent

push eventtravis-ci/docs-travis-ci-com

Artur Smirnov

commit sha 4c64d1b0c9e10c218ae98e14fba46b86dd4e34f5

Add info about config

view details

push time in 10 days

PullRequestReviewEvent

push eventtravis-ci/docs-travis-ci-com

Artur Smirnov

commit sha e6a81a76945c53001890ce0da5888d23b8196abe

Fix new line

view details

push time in 10 days

push eventtravis-ci/docs-travis-ci-com

Artur Smirnov

commit sha 1624d5d2ac8850e817e9d179cd943e7bff20c399

Add information about use of permissions

view details

push time in 10 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

+<Modal+  @isVisible={{@isOpen}}+  @onClose={{fn @onClose}}+  @closeButton={{true}}+>+  <div data-test-switchtofree-subscription-modal="true" class="flex flex--v-center flex--center flex--col switchtofree-subscription-modal">+    <h2>Looks like you are changing your plan</h2>+    <p class="switchtofree-subscription-modal__message">+      To keep improving our product and how it works for our users,<br />we’d like to hear your feedback on why you’re changing your plan+    </p>+    <h5>Reason for changing your plan</h5>+    <div class="flex switchtofree-subscription-modal__reason-options">+      {{#each this.switchToFreeReasons as |reason|}}+        <div data-test-switchtofree-reason-option='true' onClick={{action 'selectSwitchToFreeReason' reason}} class='{{if (eq selectedSwitchToFreeReason reason.name) 'selected'}}'>+          <p>{{reason.name}}</p>+        </div>+      {{/each}}+    </div>+    <div class="full-width">+      <TravisForm+        @onSubmit={{perform this.switchToFreeSubscription}}+        as |form|+      >+        <div class="form-elem switchtofree-information">+          <form.field+            @label=""+            @onChange={{action (mut this.switchToFreeReasonDetails)}}+            as |field|+          >+            <field.textarea rows="4" placeholder="Could you share more information with us?">{{this.switchToFreeReasonDetails}}</field.textarea>+          </form.field>+        </div>+        {{log }}+        <div class="form-elem switchtofree-notice">+          You are switching to the Free Trier Plan. Your remaining addon credits will be discarded and only the credits coming with the Free Trier Plan plan will be available.

The Plan plan part looks a little bit awkward to me.

piotrmilcarz

comment created time in 10 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

+<Modal+  @isVisible={{@isOpen}}+  @onClose={{fn @onClose}}+  @closeButton={{true}}+>+  <div data-test-switchtofree-subscription-modal="true" class="flex flex--v-center flex--center flex--col switchtofree-subscription-modal">+    <h2>Looks like you are changing your plan</h2>+    <p class="switchtofree-subscription-modal__message">+      To keep improving our product and how it works for our users,<br />we’d like to hear your feedback on why you’re changing your plan+    </p>+    <h5>Reason for changing your plan</h5>+    <div class="flex switchtofree-subscription-modal__reason-options">+      {{#each this.switchToFreeReasons as |reason|}}+        <div data-test-switchtofree-reason-option='true' onClick={{action 'selectSwitchToFreeReason' reason}} class='{{if (eq selectedSwitchToFreeReason reason.name) 'selected'}}'>+          <p>{{reason.name}}</p>+        </div>+      {{/each}}+    </div>+    <div class="full-width">+      <TravisForm+        @onSubmit={{perform this.switchToFreeSubscription}}+        as |form|+      >+        <div class="form-elem switchtofree-information">+          <form.field+            @label=""+            @onChange={{action (mut this.switchToFreeReasonDetails)}}+            as |field|+          >+            <field.textarea rows="4" placeholder="Could you share more information with us?">{{this.switchToFreeReasonDetails}}</field.textarea>+          </form.field>+        </div>+        {{log }}+        <div class="form-elem switchtofree-notice">+          You are switching to the Free Trier Plan. Your remaining addon credits will be discarded and only the credits coming with the Free Trier Plan plan will be available.+          {{#if (or (gt this.subscription.addonUsage.public.remainingCredits 0)+                    (gt this.subscription.addonUsage.private.remainingCredits 0))}}

I would really like to see these conditions extracted to subscription model, something like this.subscription.hasPublicCredits and this.subscription.hasPrivateCredits. They are very long and assuming the gt conditions make templates really heavy. And they repeat in many places. Although, since it works this way too, the feedback is optional.

piotrmilcarz

comment created time in 10 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

+import { module, test } from 'qunit';+import { setupRenderingTest } from 'ember-qunit';+import profilePage from 'travis/tests/pages/profile';+import { render } from '@ember/test-helpers';+import hbs from 'htmlbars-inline-precompile';++module('Integration | Component | warning-message', function (hooks) {+  setupRenderingTest(hooks);++  test('sameAddons', async function (assert) {++    const selectedPlan = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addon_configs: [+        {id: 'oss_tier_credits', name: 'Free 40 000 credits (renewed monthly)', price: 0, quantity: 40000, type: 'credit_public'},+        {id: 'free_tier_credits', name: 'Free 10 000 credits (renewed monthly)', price: 0, quantity: 10000, type: 'credit_private'}+      ]+    };++    this.selectedPlan = selectedPlan;+    this.set('selectedPlan', selectedPlan);++    const subscription = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addonUsage: {+        private: {+          remainingCredits: 10+        },+      },+      addons: [+        {name: 'Free 40 000 credits (renewed monthly)', type: 'credit_public'},+        {name: 'Free 10 000 credits (renewed monthly)', type: 'credit_private'},+      ],+    };++    this.subscription = subscription;+    this.set('subscription', subscription);+    await render(hbs`<Billing::WarningMessage+      @selectedPlan={{selectedPlan}}+      @subscription={{subscription}}+      />`);+++    assert.equal(profilePage.billing.warningMessage.text, 'You are switching to the Startup. Your remaining credits will be added to credits coming with Startup. Credits that will be added: Credits remaining balance: 10');+  });++  test('other addons', async function (assert) {++    const selectedPlan = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addon_configs: [+        {id: '1', name: 'abc', price: 0, quantity: 40000, type: 'credit_public'},+        {id: '2', name: 'xyz', price: 0, quantity: 10000, type: 'credit_private'}+      ]+    };++    this.selectedPlan = selectedPlan;+    this.set('selectedPlan', selectedPlan);++    const subscription = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addonUsage: {+        private: {+          remainingCredits: 10+        },+      },+      addons: [+        {name: 'Free 40 000 credits (renewed monthly)', type: 'credit_public'},+        {name: 'Free 10 000 credits (renewed monthly)', type: 'credit_private'},+      ],+    };++    this.subscription = subscription;+    this.set('subscription', subscription);+    await render(hbs`<Billing::WarningMessage+      @selectedPlan={{selectedPlan}}+      @subscription={{subscription}}+      />`);++    assert.equal(profilePage.billing.warningMessage.text, 'Pricing for number of users who are allowed to trigger builds may be changed. See our documentation for more details.');+  });++  test('negative ballance', async function (assert) {++    const selectedPlan = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addon_configs: [+        {id: '1', name: 'abc', price: 0, quantity: 40000, type: 'credit_public'},+        {id: '2', name: 'xyz', price: 0, quantity: 10000, type: 'credit_private'}+      ]+    };++    this.selectedPlan = selectedPlan;+    this.set('selectedPlan', selectedPlan);

And here

    this.set('selectedPlan', selectedPlan);
piotrmilcarz

comment created time in 10 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

+import { module, test } from 'qunit';+import { setupRenderingTest } from 'ember-qunit';+import profilePage from 'travis/tests/pages/profile';+import { render } from '@ember/test-helpers';+import hbs from 'htmlbars-inline-precompile';++module('Integration | Component | warning-message', function (hooks) {+  setupRenderingTest(hooks);++  test('sameAddons', async function (assert) {++    const selectedPlan = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addon_configs: [+        {id: 'oss_tier_credits', name: 'Free 40 000 credits (renewed monthly)', price: 0, quantity: 40000, type: 'credit_public'},+        {id: 'free_tier_credits', name: 'Free 10 000 credits (renewed monthly)', price: 0, quantity: 10000, type: 'credit_private'}+      ]+    };++    this.selectedPlan = selectedPlan;+    this.set('selectedPlan', selectedPlan);++    const subscription = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addonUsage: {+        private: {+          remainingCredits: 10+        },+      },+      addons: [+        {name: 'Free 40 000 credits (renewed monthly)', type: 'credit_public'},+        {name: 'Free 10 000 credits (renewed monthly)', type: 'credit_private'},+      ],+    };++    this.subscription = subscription;+    this.set('subscription', subscription);+    await render(hbs`<Billing::WarningMessage+      @selectedPlan={{selectedPlan}}+      @subscription={{subscription}}+      />`);+++    assert.equal(profilePage.billing.warningMessage.text, 'You are switching to the Startup. Your remaining credits will be added to credits coming with Startup. Credits that will be added: Credits remaining balance: 10');+  });++  test('other addons', async function (assert) {++    const selectedPlan = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addon_configs: [+        {id: '1', name: 'abc', price: 0, quantity: 40000, type: 'credit_public'},+        {id: '2', name: 'xyz', price: 0, quantity: 10000, type: 'credit_private'}+      ]+    };++    this.selectedPlan = selectedPlan;+    this.set('selectedPlan', selectedPlan);++    const subscription = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addonUsage: {+        private: {+          remainingCredits: 10+        },+      },+      addons: [+        {name: 'Free 40 000 credits (renewed monthly)', type: 'credit_public'},+        {name: 'Free 10 000 credits (renewed monthly)', type: 'credit_private'},+      ],+    };++    this.subscription = subscription;+    this.set('subscription', subscription);

And here

    this.set('subscription', subscription);
piotrmilcarz

comment created time in 10 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

+import { module, test } from 'qunit';+import { setupRenderingTest } from 'ember-qunit';+import profilePage from 'travis/tests/pages/profile';+import { render } from '@ember/test-helpers';+import hbs from 'htmlbars-inline-precompile';++module('Integration | Component | warning-message', function (hooks) {+  setupRenderingTest(hooks);++  test('sameAddons', async function (assert) {++    const selectedPlan = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addon_configs: [+        {id: 'oss_tier_credits', name: 'Free 40 000 credits (renewed monthly)', price: 0, quantity: 40000, type: 'credit_public'},+        {id: 'free_tier_credits', name: 'Free 10 000 credits (renewed monthly)', price: 0, quantity: 10000, type: 'credit_private'}+      ]+    };++    this.selectedPlan = selectedPlan;+    this.set('selectedPlan', selectedPlan);++    const subscription = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addonUsage: {+        private: {+          remainingCredits: 10+        },+      },+      addons: [+        {name: 'Free 40 000 credits (renewed monthly)', type: 'credit_public'},+        {name: 'Free 10 000 credits (renewed monthly)', type: 'credit_private'},+      ],+    };++    this.subscription = subscription;+    this.set('subscription', subscription);+    await render(hbs`<Billing::WarningMessage+      @selectedPlan={{selectedPlan}}+      @subscription={{subscription}}+      />`);+++    assert.equal(profilePage.billing.warningMessage.text, 'You are switching to the Startup. Your remaining credits will be added to credits coming with Startup. Credits that will be added: Credits remaining balance: 10');+  });++  test('other addons', async function (assert) {++    const selectedPlan = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addon_configs: [+        {id: '1', name: 'abc', price: 0, quantity: 40000, type: 'credit_public'},+        {id: '2', name: 'xyz', price: 0, quantity: 10000, type: 'credit_private'}+      ]+    };++    this.selectedPlan = selectedPlan;+    this.set('selectedPlan', selectedPlan);

Same here

    this.set('selectedPlan', selectedPlan);
piotrmilcarz

comment created time in 10 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

+<Modal+  @isVisible={{@isOpen}}+  @onClose={{fn @onClose}}+  @closeButton={{true}}+>+  <div data-test-switchtofree-subscription-modal="true" class="flex flex--v-center flex--center flex--col switchtofree-subscription-modal">+    <h2>Looks like you are changing your plan</h2>+    <p class="switchtofree-subscription-modal__message">+      To keep improving our product and how it works for our users,<br />we’d like to hear your feedback on why you’re changing your plan+    </p>+    <h5>Reason for changing your plan</h5>+    <div class="flex switchtofree-subscription-modal__reason-options">+      {{#each this.switchToFreeReasons as |reason|}}+        <div data-test-switchtofree-reason-option='true' onClick={{action 'selectSwitchToFreeReason' reason}} class='{{if (eq selectedSwitchToFreeReason reason.name) 'selected'}}'>+          <p>{{reason.name}}</p>+        </div>+      {{/each}}+    </div>+    <div class="full-width">+      <TravisForm+        @onSubmit={{perform this.switchToFreeSubscription}}+        as |form|+      >+        <div class="form-elem switchtofree-information">+          <form.field+            @label=""+            @onChange={{action (mut this.switchToFreeReasonDetails)}}+            as |field|+          >+            <field.textarea rows="4" placeholder="Could you share more information with us?">{{this.switchToFreeReasonDetails}}</field.textarea>+          </form.field>+        </div>+        {{log }}

Probably a leftover

piotrmilcarz

comment created time in 10 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

+import { module, test } from 'qunit';+import { setupRenderingTest } from 'ember-qunit';+import profilePage from 'travis/tests/pages/profile';+import { render } from '@ember/test-helpers';+import hbs from 'htmlbars-inline-precompile';++module('Integration | Component | warning-message', function (hooks) {+  setupRenderingTest(hooks);++  test('sameAddons', async function (assert) {++    const selectedPlan = {+      id: 1,+      name: 'Startup',+      startingPrice: 3000,+      startingUsers: 10,+      addon_configs: [+        {id: 'oss_tier_credits', name: 'Free 40 000 credits (renewed monthly)', price: 0, quantity: 40000, type: 'credit_public'},+        {id: 'free_tier_credits', name: 'Free 10 000 credits (renewed monthly)', price: 0, quantity: 10000, type: 'credit_private'}+      ]+    };++    this.selectedPlan = selectedPlan;+    this.set('selectedPlan', selectedPlan);

I don't think this duplication is necessary

    this.set('selectedPlan', selectedPlan);
piotrmilcarz

comment created time in 10 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

+{{#if (lt this.subscription.addonUsage.private.remainingCredits 0)}}+  <span class="notice-banner--yellow" data-test-warning-message='true'>+    Your Private or OSS Credit balance is negative. After upgrading to the {{this.selectedPlan.name}} plan the negative Credit amount

According to the condition above it seems that this message applies to Private credits only. Or did I misunderstood it?

piotrmilcarz

comment created time in 10 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

 export default Model.extend({   publicCredits: attr('number'),    isFree: equal('startingPrice', 0),-  isUnlimitedUsers: equal('startingUsers', 999999)++  isUnlimitedUsers: equal('startingUsers', 999999),++  addon_configs: attr()

This property should be in camelCase, because serializer would likely convert it (if it's processed properly)

  addonConfigs: attr()
piotrmilcarz

comment created time in 11 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

+{{#if (lt this.subscription.addonUsage.private.remainingCredits 0)}}+  <span class="notice-banner--yellow" data-test-warning-message='true'>+    Your Private or OSS Credit balance is negative. After upgrading to the {{this.selectedPlan.name}} plan the negative Credit amount+    will be deducted from the respective new credit type balance. Please see our <ExternalLinkTo @href={{this.config.urls.docs}} @title="Travis CI Docs">documentation</ExternalLinkTo> for more details.+  </span>+{{else if (gt this.subscription.addonUsage.private.remainingCredits 0)}}+  {{#if this.sameAddons}}+    <span class="notice-banner--yellow" data-test-warning-message='true'>+      You are switching to the {{this.selectedPlan.name}}. Your remaining credits will be added to credits coming with {{this.selectedPlan.name}}.+      Credits that will be added:+      {{#if (gt this.subscription.addonUsage.public.remainingCredits 0)}}+        OSS Credits remaining balance: {{this.subscription.addonUsage.public.remainingCredits}}+      {{/if}}+      {{#if (gt this.subscription.addonUsage.private.remainingCredits 0)}}

Assuming the condition above, this will always be true

piotrmilcarz

comment created time in 10 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

 export default Component.extend({     this.flashes.error(message);   }, +  closeSwitchToModal: task(function* () {+    this.set('showSwitchToFreeModal', false);+    this.storage.clearBillingData();+    yield this.set('showPlansSelector', false);

It doesn't seem like yield makes any sense here. set is completely synchronous operation. And the entire task doesn't need to be a task, does it? Could it be just a plain method/action?

piotrmilcarz

comment created time in 11 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

+{{#if (lt this.subscription.addonUsage.private.remainingCredits 0)}}+  <span class="notice-banner--yellow" data-test-warning-message='true'>+    Your Private or OSS Credit balance is negative. After upgrading to the {{this.selectedPlan.name}} plan the negative Credit amount+    will be deducted from the respective new credit type balance. Please see our <ExternalLinkTo @href={{this.config.urls.docs}} @title="Travis CI Docs">documentation</ExternalLinkTo> for more details.

Would it make sense to bind this link to this url https://github.com/travis-ci/travis-web/blob/epic-pricing/config/environment.js#L113?

piotrmilcarz

comment created time in 10 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

 export default Model.extend({     return yield this.api.post(`/v2_subscription/${this.id}/pay`);   }).drop(), +  switchToFreeSubscription: task(function* (data) {+    yield this.api.post(`/v2_subscription/${this.id}/changetofree`, {+      data+    });+    yield this.accounts.fetchV2Subscriptions.perform();+  }).drop(),

Could you please make tasks interfaces more explicit? We don't want the entire application to need to be aware of the API shape. If it's outside of Ember Data, let's at least encapsulate the serialization logic inside the task:

  switchToFreeSubscription: task(function* (reason, details) {
    yield this.api.post(`/v2_subscription/${this.id}/changetofree`, {
      data: { reason, reason_details: details }
    });
    yield this.accounts.fetchV2Subscriptions.perform();
  }).drop(),

And then you can call it like this: this.subscription.switchToFreeSubscription.perform(reason, details)

piotrmilcarz

comment created time in 11 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

+import Component from '@ember/component';+import { computed } from '@ember/object';++export default Component.extend({+  sameAddons: computed('subscription.addons.[]', 'selectedPlan.addon_configs.[]', function () {+    if (!this.selectedPlan.addon_configs || !this.subscription.addons)+      return false;+    let sameAddon = false;+    this.selectedPlan.addon_configs.forEach(addonConfig => {+      this.subscription.addons.forEach(addon => {+        if (addon.name === addonConfig.name && addon.type === addonConfig.type)+          sameAddon = true;+      });+    });+    return sameAddon;

I'd like to recommend a little optimization:

    return this.selectedPlan.addon_configs.any(addonConfig => (
      this.subscription.addons.any(addon => (
        addon.name === addonConfig.name && addon.type === addonConfig.type
      ));
    ));

This way it'll use short circuit to validate the condition and won't iterate over both arrays every time, even if the required condition is already found. But this is very minor and completely optional feedback.

piotrmilcarz

comment created time in 11 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

 export default Component.extend({     this.flashes.error(message);   }, +  closeSwitchToModal: task(function* () {

Should it be

  closeSwitchToFreeModal: task(function* () {

?

piotrmilcarz

comment created time in 11 days

Pull request review commenttravis-ci/travis-web

Corner cases handling

 export default Component.extend({   }),    updatePlan: task(function* () {-    yield this.subscription.changePlan.perform(this.selectedPlan.id);-    yield this.accounts.fetchV2Subscriptions.perform();-    yield this.retryAuthorization.perform();-    this.storage.clearBillingData();-    this.set('showPlansSelector', false);+    if (this.selectedPlan.startingPrice > 0) {

Please use plan.isFree property to determine if the plan is free. The definition of "free" may change over time.

piotrmilcarz

comment created time in 11 days

PullRequestReviewEvent
PullRequestReviewEvent

push eventtravis-ci/travis-web

AndriiMysko

commit sha 0d3abf83c9621bce8e679469ea1b725000bdc36d

Fix plan page route (#2540) * Fix plan page route * Fix failing spec

view details

push time in 11 days

PR merged travis-ci/travis-web

Fix plan page route

Hello @artursmirnov This PR should fix the merged version of my previous PR and epic-pricing, since you mentioned that it didn't work. Also I've adjusted some icons in the plan selection page. Please take a look.

+16 -11

2 comments

6 changed files

AndriiMysko

pr closed time in 11 days

PullRequestReviewEvent
PullRequestReviewEvent

pull request commenttravis-ci/travis-web

Fix plan page route

Also there's a test failure that might be related to your changes: https://travis-ci.com/github/travis-ci/travis-web/builds/184208595#L327

AndriiMysko

comment created time in 11 days

PullRequestReviewEvent

create barnchtravis-ci/travis-web

branch : pricing-v2-staging

created branch time in 12 days

delete branch travis-ci/travis-web

delete branch : staging

delete time in 12 days

PR closed travis-ci/travis-web

Staging do_not_merge
+639 -26

0 comment

22 changed files

artursmirnov

pr closed time in 12 days

delete branch travis-ci/travis-web

delete branch : pricing-v2-staging

delete time in 12 days

push eventtravis-ci/travis-web

Artur Smirnov

commit sha 9d4013c78b5ddacb909c24da278cebc7c34cfd9f

Update VAT field display according to new requirements (#2532) * Update VAT field display according to new requirements * Fix tests * Add an attribute to pass local registration flag to API * Fix address form for new subscription, add new VAT rules to edit billing address form * Fix form errors

view details

Artur Smirnov

commit sha 4ff14a4c9db057763cbfd453c7f84a75083ffbf2

Merge branch 'master' into epic-pricing

view details

push time in 12 days

push eventtravis-ci/travis-web

Artur Smirnov

commit sha 9d4013c78b5ddacb909c24da278cebc7c34cfd9f

Update VAT field display according to new requirements (#2532) * Update VAT field display according to new requirements * Fix tests * Add an attribute to pass local registration flag to API * Fix address form for new subscription, add new VAT rules to edit billing address form * Fix form errors

view details

push time in 12 days

delete branch travis-ci/travis-web

delete branch : as-vat-adjustments

delete time in 12 days

PR merged travis-ci/travis-web

Update VAT field display according to new requirements

Ticket: https://github.com/travis-pro/billing-v2/issues/588

+209 -175

4 comments

9 changed files

artursmirnov

pr closed time in 12 days

pull request commenttravis-ci/travis-web

Update VAT field display according to new requirements

@shairyar are API changes already released? Shall I merge and release this PR as well?

artursmirnov

comment created time in 14 days

pull request commenttravis-ci/travis-web

Changes for V2 Plan summary and change pages

Awesome! @AndriiMysko and thank you for your hard work on this PR 👍

AndriiMysko

comment created time in 14 days

more