Skip to content

Commit

Permalink
Merge pull request #525 from appuio/create-be
Browse files Browse the repository at this point in the history
Create Billing
  • Loading branch information
ccremer authored Apr 17, 2023
2 parents 0b30e34 + a540249 commit 0296d49
Show file tree
Hide file tree
Showing 41 changed files with 1,222 additions and 174 deletions.
4 changes: 2 additions & 2 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@
"budgets": [
{
"type": "initial",
"maximumWarning": "1500kb",
"maximumError": "2mb"
"maximumWarning": "2mb",
"maximumError": "3mb"
},
{
"type": "anyComponentStyle",
Expand Down
7 changes: 7 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defineConfig } from 'cypress';
import * as installLogsPrinter from 'cypress-terminal-report/src/installLogsPrinter';

export default defineConfig({
videosFolder: 'cypress/videos',
Expand All @@ -7,5 +8,11 @@ export default defineConfig({
retries: 2,
e2e: {
baseUrl: 'http://localhost:4200',
// eslint-disable-next-line @typescript-eslint/no-unused-vars
setupNodeEvents(on, config) {
installLogsPrinter(on, {
printLogsToConsole: 'always',
});
},
},
});
19 changes: 14 additions & 5 deletions cypress/e2e/billingentities.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ describe('Test billing entity list', () => {
cy.intercept('GET', 'appuio-api/apis/appuio.io/v1/users/mig', {
body: createUser({ username: 'mig', defaultOrganizationRef: 'nxt' }),
});
cy.setPermission({ verb: 'list', ...BillingEntityPermissions });
cy.setPermission({ verb: 'list', ...BillingEntityPermissions }, { verb: 'create', ...BillingEntityPermissions });
});

it('list with two entries', () => {
setBillingEntities(cy, billingEntityNxt, billingEntityVshn);
cy.visit('/billingentities');
cy.get('#billingentities-title').should('contain.text', 'Billing');
cy.get('#addButton').should('contain.text', 'Add new Billing');
cy.get(':nth-child(2) > .flex-row > .text-3xl').should('contain.text', 'be-2345');
cy.get(':nth-child(3) > .flex-row > .text-3xl').should('contain.text', 'be-2347');
});
Expand All @@ -28,6 +29,7 @@ describe('Test billing entity list', () => {
setBillingEntities(cy);
cy.visit('/billingentities');
cy.get('#billingentities-title').should('contain.text', 'Billing');
cy.get('#addButton').should('contain.text', 'Add new Billing');
cy.get('#no-billingentity-message').should('contain.text', 'No billing entities available.');
});

Expand All @@ -36,7 +38,6 @@ describe('Test billing entity list', () => {
statusCode: 403,
});
cy.visit('/billingentities');
cy.get('#billingentities-title').should('contain.text', 'Billing');
cy.get('#failure-message').should('contain.text', 'Billing entities could not be loaded.');
});

Expand Down Expand Up @@ -83,6 +84,14 @@ describe('no permissions', () => {
cy.visit('/billingentities/be-2345/members');
cy.get('h1').should('contain.text', 'Welcome to the APPUiO Cloud Portal');
});

it('no create permission', () => {
setBillingEntities(cy);
cy.setPermission({ verb: 'list', ...BillingEntityPermissions });
cy.visit('/billingentities');
cy.get('h1').should('contain.text', 'Billing');
cy.get('addButton').should('not.exist');
});
});

describe('Test billing entity details', () => {
Expand Down Expand Up @@ -113,10 +122,10 @@ describe('Test billing entity details', () => {
});
cy.visit('/billingentities/be-2345');
cy.get('.flex-wrap > .text-900').eq(0).should('contain.text', '➡️ Engineering GmbH');
cy.get('.flex-wrap > .text-900').eq(1).should('contain.text', '📧');
cy.get('.flex-wrap > .text-900').eq(1).should('contain.text', '[email protected]');
cy.get('.flex-wrap > .text-900').eq(2).should('contain.text', '☎️');
cy.get('.flex-wrap > .text-900').eq(3).should('contain.text', '📃📋🏤 🏙️🇨🇭');
cy.get('.flex-wrap > .text-900').eq(4).should('contain.text', 'mig 📧');
cy.get('.flex-wrap > .text-900').eq(3).should('contain.text', '📃📋🏤 🏙️Switzerland');
cy.get('.flex-wrap > .text-900').eq(4).should('contain.text', 'mig [email protected]');
cy.get('.flex-wrap > .text-900').eq(5).should('contain.text', '🇩🇪');
});
});
208 changes: 208 additions & 0 deletions cypress/e2e/billingentity-form.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import { createUser } from '../fixtures/user';
import { BillingEntity, BillingEntityPermissions, BillingEntitySpec } from '../../src/app/types/billing-entity';
import { billingEntityNxt, setBillingEntities } from '../fixtures/billingentities';

describe('Test billing entity form elements', () => {
beforeEach(() => {
cy.setupAuth();
window.localStorage.setItem('hideFirstTimeLoginDialog', 'true');
cy.disableCookieBanner();
});
beforeEach(() => {
// needed for initial getUser request
cy.intercept('GET', 'appuio-api/apis/appuio.io/v1/users/mig', {
body: createUser({ username: 'mig', defaultOrganizationRef: 'nxt' }),
});
cy.setPermission({ verb: 'list', ...BillingEntityPermissions }, { verb: 'create', ...BillingEntityPermissions });
});

it('mark all fields required', () => {
cy.visit('/billingentities/$new?edit=y');
cy.get('#title').should('contain.text', 'New Billing');
cy.get('button[type="submit"]').should('be.disabled');

cy.get('#displayName')
.type('a')
.should('have.class', 'ng-invalid')
.type('b')
.should('not.have.class', 'ng-invalid');

cy.get('#companyEmail').find('input').type('a{enter}');
cy.get('#companyEmail').should('have.class', 'ng-invalid');
cy.get('#companyEmail').find('input').type('{backspace}info@company,');
cy.get('#companyEmail').should('not.have.class', 'ng-invalid');

cy.get('#phone')
.type('1{backspace}')
.should('have.class', 'ng-invalid')
.type('1234')
.should('not.have.class', 'ng-invalid');

cy.get('#line1')
.type('1{backspace}')
.should('have.class', 'ng-invalid')
.type('line1')
.should('not.have.class', 'ng-invalid');

cy.get('#line2').type('2{backspace}').should('not.have.class', 'ng-invalid').type('line2'); // not required

cy.get('#postal')
.type('p{backspace}')
.should('have.class', 'ng-invalid')
.type('postal')
.should('not.have.class', 'ng-invalid');

cy.get('#city')
.type('c{backspace}')
.should('have.class', 'ng-invalid')
.type('city')
.should('not.have.class', 'ng-invalid');

cy.get('#accountingName')
.type('a{backspace}')
.should('have.class', 'ng-invalid')
.type('accounting')
.should('not.have.class', 'ng-invalid');

cy.get('button[type="submit"]').should('be.disabled');
cy.get('p-dropdown').click().contains('Switzerland').click();
cy.get('button[type="submit"]').should('be.enabled');
});

it('should validate emails', () => {
cy.visit('/billingentities/$new?edit=y');
cy.get('#title').should('contain.text', 'New Billing');

cy.get('.p-checkbox-box').click();

['#companyEmail', '#accountingEmail'].forEach((e) => {
cy.get(e).find('input').type('a{enter}');
cy.get(e).should('have.class', 'ng-invalid');
cy.get(e).find('input').type('{backspace}info@company,');
cy.get(e).should('not.have.class', 'ng-invalid');
cy.get(e).find('input').type('b{enter}');
cy.get(e).should('have.class', 'ng-invalid');
cy.get(e).find('input').type('{backspace}another@tld,');
cy.get(e).should('not.have.class', 'ng-invalid');
});
});

it('should copy emails from company', () => {
cy.visit('/billingentities/$new?edit=y');
cy.get('#title').should('contain.text', 'New Billing');

cy.get('#companyEmail').find('input').type('info@company,');
cy.get('#accountingEmail').find('ul').should('have.class', 'p-disabled').should('contain.text', 'info@company');

cy.get('#companyEmail').find('input').type('hello@company{enter}');
cy.get('#accountingEmail')
.find('ul')
.should('contain.text', 'info@company')
.should('contain.text', 'hello@company');

cy.get('.p-checkbox-box').click();

cy.get('#companyEmail').find('input').type('{backspace}');
cy.get('#companyEmail').should('not.contain.text', 'hello@company');
cy.get('#accountingEmail')
.find('ul')
.should('not.have.class', 'p-disabled')
.should('contain.text', 'info@company')
.should('contain.text', 'hello@company');

cy.get('#accountingEmail').find('input').type('{backspace}{backspace}');
cy.get('#accountingEmail').should('have.class', 'ng-invalid');
cy.get('#accountingEmail').find('input').type('accounting@company,');
cy.get('#accountingEmail').should('not.have.class', 'ng-invalid');

cy.get('.p-checkbox-box').click();

cy.get('#accountingEmail')
.find('ul')
.should('have.class', 'p-disabled')
.should('contain.text', 'info@company')
.should('not.contain.text', 'hello@company')
.should('not.contain.text', 'accounting@company');
});

it('should cancel editing', () => {
setBillingEntities(cy);

['.p-button-secondary', 'a[appbacklink]'].forEach((cancelSelector) => {
cy.visit('/billingentities/$new?edit=y');
cy.get('#title').should('contain.text', 'New Billing');

cy.get(cancelSelector).click();
cy.get('app-billingentity-form').should('not.exist');

cy.get('p-messages').should('contain.text', 'No billing entities available');
});
});
});

describe('Test billing entity create', () => {
beforeEach(() => {
cy.setupAuth();
window.localStorage.setItem('hideFirstTimeLoginDialog', 'true');
cy.disableCookieBanner();
});
beforeEach(() => {
// needed for initial getUser request
cy.intercept('GET', 'appuio-api/apis/appuio.io/v1/users/mig', {
body: createUser({ username: 'mig', defaultOrganizationRef: 'nxt' }),
});
cy.setPermission({ verb: 'list', ...BillingEntityPermissions }, { verb: 'create', ...BillingEntityPermissions });
});

it('should submit form values', () => {
cy.intercept('POST', '/appuio-api/apis/billing.appuio.io/v1/billingentities', {
body: billingEntityNxt,
}).as('createBillingEntity');

cy.visit('/billingentities/$new?edit=y');
cy.get('#title').should('contain.text', 'New Billing');

cy.get('#displayName').type('➡️ Engineering GmbH');

cy.get('#companyEmail').find('input').type('[email protected],');
cy.get('#phone').type('☎️');
cy.get('#line1').type('📃');
cy.get('#line2').type('📋');
cy.get('#postal').type('🏤');
cy.get('#city').type('🏙️');
cy.get('p-dropdown').click().contains('Switzerland').click();

cy.get('#accountingName').type('mig');

cy.get('button[type="submit"]').should('be.enabled').click();
cy.wait('@createBillingEntity')
.its('request.body')
.then((body: BillingEntity) => {
expect(body.metadata.generateName).eq('be-');
expect(body.metadata.name).empty; // ensure no metadata name is set for new objects
const expected: BillingEntitySpec = {
name: '➡️ Engineering GmbH',
phone: '☎️',
emails: ['[email protected]'],
address: {
line1: '📃',
line2: '📋',
postalCode: '🏤',
city: '🏙️',
country: 'Switzerland',
},
accountingContact: {
name: 'mig',
emails: ['[email protected]'],
},
};
console.debug('expected', expected);
console.debug('actual', body.spec);
expect(body.spec).deep.eq(expected);
});

cy.get('p-toast').should('contain.text', 'Successfully saved');
cy.get('#title').should('contain.text', 'be-2345');
cy.url().should('include', '/billingentities/be-2345');
});
});
6 changes: 3 additions & 3 deletions cypress/fixtures/billingentities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ export const billingEntityNxt: BillingEntity = {
line2: '📋',
postalCode: '🏤',
city: '🏙️',
country: '🇨🇭',
country: 'Switzerland',
},
emails: ['📧'],
emails: ['[email protected]'],
phone: '☎️',
languagePreference: '🇩🇪',
accountingContact: {
name: 'mig',
emails: ['📧'],
emails: ['[email protected]'],
},
},
};
Expand Down
5 changes: 5 additions & 0 deletions cypress/support/e2e.ts
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
import './commands';
import * as installLogsCollector from 'cypress-terminal-report/src/installLogsCollector';

installLogsCollector({
collectTypes: ['cons:log', 'cons:info', 'cons:warn', 'cons:error', 'cy:log', 'cy:command'],
});
11 changes: 10 additions & 1 deletion deployment/helmfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ helmDefaults:
releases:
- name: "{{ env "HELM_RELEASE_NAME" | default "portal" }}"
chart: appuio/cloud-portal
version: v0.2.0
version: v0.4.1
createNamespace: false
missingFileHandler: Warn
values:
Expand Down Expand Up @@ -56,3 +56,12 @@ releases:
kubernetesAPI: Kubernetes API
oauth: API Token
consoleUrlKey: 'console'
countries:
- code: CH
name: Switzerland
- code: DE
name: Germany
- code: AT
name: Austria
- code: LI
name: Liechtenstein
Loading

0 comments on commit 0296d49

Please sign in to comment.