diff --git a/.editorconfig b/.editorconfig index 219985c2..12b0275a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,8 +13,5 @@ insert_final_newline = true indent_style = space indent_size = 2 -[*.hbs] -insert_final_newline = false - [*.{diff,md}] trim_trailing_whitespace = false diff --git a/app/components/build-result-summary-table.hbs b/app/components/build-result-summary-table.hbs new file mode 100644 index 00000000..3e75e092 --- /dev/null +++ b/app/components/build-result-summary-table.hbs @@ -0,0 +1,13 @@ + + + + + + + + + {{#each this.scenarios as |scenario|}} + + {{/each}} + +
ScenarioResult
diff --git a/app/components/build-result-summary-table.js b/app/components/build-result-summary-table.js new file mode 100644 index 00000000..34b4371d --- /dev/null +++ b/app/components/build-result-summary-table.js @@ -0,0 +1,11 @@ +import classic from 'ember-classic-decorator'; +import { tagName } from '@ember-decorators/component'; +import { readOnly } from '@ember/object/computed'; +import Component from '@ember/component'; + +@classic +@tagName('') +export default class BuildResultSummaryTable extends Component { + @readOnly('results.scenarios') + scenarios; +} diff --git a/app/components/build-result-summary-table/row.hbs b/app/components/build-result-summary-table/row.hbs new file mode 100644 index 00000000..53ad92fe --- /dev/null +++ b/app/components/build-result-summary-table/row.hbs @@ -0,0 +1,13 @@ + + {{@scenario.scenarioName}} + + {{#if @scenario.passed}} + Passed + {{else}} + Failed + {{#if @scenario.allowedToFail}} + (allowed) + {{/if}} + {{/if}} + + diff --git a/app/components/build-result-summary-table/row.js b/app/components/build-result-summary-table/row.js new file mode 100644 index 00000000..d8de185e --- /dev/null +++ b/app/components/build-result-summary-table/row.js @@ -0,0 +1,13 @@ +import Component from '@glimmer/component'; + +export default class BuildResultSummaryTableRow extends Component { + get testResultClass() { + if (this.args.scenario.passed) { + return 'passed'; + } + if (this.args.scenario.allowedToFail) { + return 'allowed-failure'; + } + return 'failed'; + } +} diff --git a/app/models/test-result.js b/app/models/test-result.js index bcc3b42a..021045d0 100644 --- a/app/models/test-result.js +++ b/app/models/test-result.js @@ -25,6 +25,9 @@ export default class TestResult extends Model { @attr('string') semverString; + @attr() + emberTryResults; + @belongsTo('version') version; diff --git a/app/styles/_canary_test_results.scss b/app/styles/_canary_test_results.scss index 6e8bb75f..09ebe7ac 100644 --- a/app/styles/_canary_test_results.scss +++ b/app/styles/_canary_test_results.scss @@ -26,7 +26,7 @@ background-color: #e61313; } - &.error { + &.error, &.allowed-failure { background-color: #9e9e9e; } } diff --git a/app/styles/app.scss b/app/styles/app.scss index 712d80be..bdf4afa5 100644 --- a/app/styles/app.scss +++ b/app/styles/app.scss @@ -15,6 +15,7 @@ @import "addons_index"; @import "addons-list"; @import "components/build-result-output"; +@import "components/build-result-summary-table"; @import "components/code-search"; @import "components/dependency-table"; @import "components/large-search"; diff --git a/app/styles/components/_build-result-summary-table.scss b/app/styles/components/_build-result-summary-table.scss new file mode 100644 index 00000000..f581c3c8 --- /dev/null +++ b/app/styles/components/_build-result-summary-table.scss @@ -0,0 +1,14 @@ +.build-result-summary-table { + width: auto; + + td { + padding: 0.75em; + } + + th:first-child { + width: 15em; + } + th:not(:first-child) { + width: 8em; + } +} diff --git a/app/templates/admin/build-results/show.hbs b/app/templates/admin/build-results/show.hbs index 91c51a59..366d1482 100644 --- a/app/templates/admin/build-results/show.hbs +++ b/app/templates/admin/build-results/show.hbs @@ -23,6 +23,11 @@ + {{#if this.buildResult.emberTryResults}} +

Results

+ + {{/if}} +

Output

diff --git a/tests/acceptance/build-results-test.js b/tests/acceptance/build-results-test.js index e8910852..80735a5e 100644 --- a/tests/acceptance/build-results-test.js +++ b/tests/acceptance/build-results-test.js @@ -2,6 +2,7 @@ import { findAll, click, currentURL, currentRouteName, visit } from '@ember/test import { module, test } from 'qunit'; import { percySnapshot } from 'ember-percy'; import { setupEmberObserverTest } from '../helpers/setup-ember-observer-test'; +import emberTryScenario from 'ember-observer/tests/helpers/ember-try-scenario'; import findByText from '../helpers/find-by-text'; import login from 'ember-observer/tests/helpers/login'; import moment from 'moment'; @@ -9,234 +10,264 @@ import moment from 'moment'; module('Acceptance | build results', function(hooks) { setupEmberObserverTest(hooks); - test('displays basic info about a build', async function(assert) { - let addon = server.create('addon'); - let addonVersion = server.create('version', { addonId: addon.id }); - server.create('testResult', { - versionId: addonVersion.id, - createdAt: moment('2016-08-07 16:30').utc() + module('Index', function() { + test('displays basic info about a build', async function(assert) { + let addon = server.create('addon'); + let addonVersion = server.create('version', { addon }); + server.create('testResult', { + version: addonVersion, + createdAt: moment('2016-08-07 16:30').utc() + }); + + await login(); + await visit('/admin/build-results'); + + assert.equal(currentRouteName(), 'admin.build-results.index'); + let results = findAll('.test-build-result td'); + assert.dom(results[0]).hasText(addon.name, 'displays addon name'); + assert.dom(results[1]).hasText('1.0.0', 'displays addon version'); + assert.dom(results[2]).hasText('2016-08-07 16:30', 'displays date/time'); }); - await login(); - await visit('/admin/build-results'); - - assert.equal(currentRouteName(), 'admin.build-results.index'); - let results = findAll('.test-build-result td'); - assert.dom(results[0]).hasText(addon.name, 'displays addon name'); - assert.dom(results[1]).hasText('1.0.0', 'displays addon version'); - assert.dom(results[2]).hasText('2016-08-07 16:30', 'displays date/time'); - }); - - test('sorts results by run date', async function(assert) { - let addon = server.create('addon'); - let addonVersion = server.create('version', { addonId: addon.id }); - let middleTestResult = server.create('testResult', { - createdAt: moment('2016-11-19 12:00:00').utc() - }); - let earliestTestResult = server.create('testResult', { - createdAt: moment('2016-11-19 00:00:01').utc() - }); - let latestTestResult = server.create('testResult', { - createdAt: moment('2016-11-19 23:59:59').utc() + test('sorts results by run date', async function(assert) { + let addon = server.create('addon'); + let addonVersion = server.create('version', { addon }); + let middleTestResult = server.create('testResult', { + version: addonVersion, + createdAt: moment('2016-11-19 12:00:00').utc() + }); + let earliestTestResult = server.create('testResult', { + version: addonVersion, + createdAt: moment('2016-11-19 00:00:01').utc() + }); + let latestTestResult = server.create('testResult', { + version: addonVersion, + createdAt: moment('2016-11-19 23:59:59').utc() + }); + + await login(); + await visit('/admin/build-results'); + + assert.dom('.test-build-result').hasAttribute('data-testResultId', `${latestTestResult.id}`); + + let results = findAll('.test-build-result'); + assert.dom(results[1]).hasAttribute('data-testResultId', `${middleTestResult.id}`); + assert.dom(results[2]).hasAttribute('data-testResultId', `${earliestTestResult.id}`); }); - addonVersion.update({ - testResultIds: [middleTestResult.id, earliestTestResult.id, latestTestResult.id] + + test('displays appropriate status based on result', async function(assert) { + let addon = server.create('addon'); + let addonVersion = server.create('version', { addon }); + server.create('testResult', { + version: addonVersion, + succeeded: false, + statusMessage: 'timed out', + createdAt: moment().subtract(30, 'minutes').utc() + }); + server.create('testResult', { + version: addonVersion, + succeeded: true, + createdAt: moment().subtract(1, 'hour').utc() + }); + + await login(); + await visit('/admin/build-results'); + + let results = findAll('.test-build-result'); + assert.dom(results[0]).containsText('failed - timed out', 'displays failure notice with status message for failed builds'); + assert.dom(results[1]).containsText('succeeded', 'displays "succeeded" for successful builds'); }); - await login(); - await visit('/admin/build-results'); + test('displays semver string for non-canary builds', async function(assert) { + let addon = server.create('addon'); + let addonVersion = server.create('version', { addon }); + server.create('testResult', { + version: addonVersion, + canary: false, + semverString: '>= 2.0.0', + createdAt: moment('2016-08-07 16:30').utc() + }); - assert.dom('.test-build-result').hasAttribute('data-testResultId', `${latestTestResult.id}`); + await login(); + await visit('/admin/build-results'); - let results = findAll('.test-build-result'); - assert.dom(results[1]).hasAttribute('data-testResultId', `${middleTestResult.id}`); - assert.dom(results[2]).hasAttribute('data-testResultId', `${earliestTestResult.id}`); - }); + assert.dom('.test-build-result').containsText('>= 2.0.0', 'displays semver string'); - test('displays appropriate status based on result', async function(assert) { - let addon = server.create('addon'); - let addonVersion = server.create('version', { addonId: addon.id }); - let timedOutResult = server.create('testResult', { - succeeded: false, - statusMessage: 'timed out', - createdAt: moment().subtract(30, 'minutes').utc() - }); - let succeededResult = server.create('testResult', { - succeeded: true, - createdAt: moment().subtract(1, 'hour').utc() - }); - addonVersion.update({ - testResultIds: [timedOutResult.id, succeededResult.id] + await click(findByText('.test-build-result a', 'details')); + + assert.dom('.test-semver-string').hasText('>= 2.0.0', 'displays semver string'); }); - await login(); - await visit('/admin/build-results'); + test('displays appropriate indication for canary builds', async function(assert) { + let addon = server.create('addon'); + let addonVersion = server.create('version', { addon }); + server.create('testResult', { + version: addonVersion, + canary: true, + createdAt: moment('2016-08-07 16:30').utc() + }); - let results = findAll('.test-build-result'); - assert.dom(results[0]).containsText('failed - timed out', 'displays failure notice with status message for failed builds'); - assert.dom(results[1]).containsText('succeeded', 'displays "succeeded" for successful builds'); - }); + await login(); + await visit('/admin/build-results'); + + assert.dom('.test-build-result').containsText('canary', 'displays indication for canary builds on list'); - test('displays semver string for non-canary builds', async function(assert) { - let addon = server.create('addon'); - let addonVersion = server.create('version', { addonId: addon.id }); - server.create('testResult', { - versionId: addonVersion.id, - canary: false, - semverString: '>= 2.0.0', - createdAt: moment('2016-08-07 16:30').utc() + await click(findByText('.test-build-result a', 'details')); + assert.dom('.test-semver-string').hasText('canary', 'displays indication for canary builds in detail'); }); - await login(); - await visit('/admin/build-results'); + test('links to previous day', async function(assert) { + await login(); + await visit('/admin/build-results?date=2017-02-01'); - assert.dom('.test-build-result').containsText('>= 2.0.0', 'displays semver string'); + assert.dom('a[href="/admin/build-results?date=2017-01-31"]').exists('has a link to the results for the previous day'); + }); - await click(findByText('.test-build-result a', 'details')); + test('links to following day if not viewing the current date', async function(assert) { + await login(); + await visit('/admin/build-results?date=2016-11-18'); - assert.dom('.test-semver-string').hasText('>= 2.0.0', 'displays semver string'); - }); - - test('displays appropriate indication for canary builds', async function(assert) { - let addon = server.create('addon'); - let addonVersion = server.create('version', { addonId: addon.id }); - server.create('testResult', { - versionId: addonVersion.id, - canary: true, - createdAt: moment('2016-08-07 16:30').utc() + assert.dom('a[href="/admin/build-results?date=2016-11-19"]').exists('has a link to the results for the following day'); }); - await login(); - await visit('/admin/build-results'); + test('does not link to following day if viewing the current date', async function(assert) { + let tomorrow = moment().add(1, 'day').utc().format('Y-M-D'); - assert.dom('.test-build-result').containsText('canary', 'displays indication for canary builds on list'); + await login(); + await visit('/admin/build-results'); - await click(findByText('.test-build-result a', 'details')); - assert.dom('.test-semver-string').hasText('canary', 'displays indication for canary builds in detail'); - }); + assert.dom(`a[href="/admin/build-results?date=${tomorrow}"]`).doesNotExist('does not have a link to the results for the following day'); + }); - test('links to previous day', async function(assert) { - await login(); - await visit('/admin/build-results?date=2017-02-01'); + test('links to detail for individual builds', async function(assert) { + let version = server.create('version'); + let testResult = server.create('testResult', { version }); - assert.dom('a[href="/admin/build-results?date=2017-01-31"]').exists('has a link to the results for the previous day'); - }); + await login(); + await visit('/admin/build-results'); + await click(findByText('.test-build-result a', 'details')); - test('links to following day if not viewing the current date', async function(assert) { - await login(); - await visit('/admin/build-results?date=2016-11-18'); + await percySnapshot('/admin/build-results'); - assert.dom('a[href="/admin/build-results?date=2016-11-19"]').exists('has a link to the results for the following day'); + assert.equal(currentURL(), `/admin/build-results/${testResult.id}`); + }); }); - test('does not link to following day if viewing the current date', async function(assert) { - let tomorrow = moment().add(1, 'day').utc().format('Y-M-D'); + module('Individual build detail', function() { + test('detail page shows data for a build', async function(assert) { + let addon = server.create('addon'); + let version = server.create('version', { + addon + }); + let testResult = server.create('testResult', { + version, + output: 'this is the output', + createdAt: moment('2016-08-01 12:34:56').utc() + }); + + await login(); + await visit(`/admin/build-results/${testResult.id}`); + + assert.dom('.test-addon-name').hasText(addon.name, 'displays addon name'); + assert.dom('.test-addon-version').hasText(version.version, 'displays addon version'); + assert.dom('.test-run-date').hasText('2016-08-01 12:34', 'displays date/time tests ran'); + assert.dom('.test-output').hasText('this is the output', "displays result's output"); + }); - await login(); - await visit('/admin/build-results'); + test('detail page shows "succeeded" for status when build succeeded', async function(assert) { + let version = server.create('version'); + let testResult = server.create('testResult', { + version, + succeeded: true + }); - assert.dom(`a[href="/admin/build-results?date=${tomorrow}"]`).doesNotExist('does not have a link to the results for the following day'); - }); + await login(); + await visit(`/admin/build-results/${testResult.id}`); - test('links to detail for individual builds', async function(assert) { - let version = server.create('version'); - let testResult = server.create('testResult', { versionId: version.id }); + assert.dom('.test-build-status').hasText('succeeded', 'displays "succeeded" for build status'); + }); - await login(); - await visit('/admin/build-results'); - await click(findByText('.test-build-result a', 'details')); + test('detail page shows status message when build did not succeeded', async function(assert) { + let version = server.create('version'); + let testResult = server.create('testResult', { + version, + succeeded: false, + statusMessage: 'this is the status' + }); - await percySnapshot('/admin/build-results'); + await login(); + await visit(`/admin/build-results/${testResult.id}`); - assert.equal(currentURL(), `/admin/build-results/${testResult.id}`); - }); - - test('detail page shows data for a build', async function(assert) { - let addon = server.create('addon'); - let version = server.create('version', { - addonId: addon.id - }); - let testResult = server.create('testResult', { - versionId: version.id, - output: 'this is the output', - createdAt: moment('2016-08-01 12:34:56').utc() + assert.dom('.test-build-status').hasText('this is the status', 'displays status message for the build'); }); - server.db.versions.update(version, { testResultId: testResult.id }); - await login(); - await visit(`/admin/build-results/${testResult.id}`); + test('detail page has a "retry" button for failed builds', async function(assert) { + assert.expect(3); - assert.dom('.test-addon-name').hasText(addon.name, 'displays addon name'); - assert.dom('.test-addon-version').hasText(version.version, 'displays addon version'); - assert.dom('.test-run-date').hasText('2016-08-01 12:34', 'displays date/time tests ran'); - assert.dom('.test-output').hasText('this is the output', "displays result's output"); - }); + let version = server.create('version'); + let testResult = server.create('testResult', { + version, + succeeded: false, + statusMessage: 'failed' + }); - test('detail page shows "succeeded" for status when build succeeded', async function(assert) { - let version = server.create('version'); - let testResult = server.create('testResult', { - versionId: version.id, - succeeded: true - }); - server.db.versions.update(version, { testResultId: testResult.id }); + server.post('/test_results/:id/retry', function() { + assert.ok(true, 'makes retry request'); + }); - await login(); - await visit(`/admin/build-results/${testResult.id}`); + await login(); + await visit(`/admin/build-results/${testResult.id}`); - assert.dom('.test-build-status').hasText('succeeded', 'displays "succeeded" for build status'); - }); + assert.dom('.test-retry-build').exists('"retry" button exists'); + + await click('.test-retry-build'); - test('detail page shows status message when build did not succeeded', async function(assert) { - let version = server.create('version'); - let testResult = server.create('testResult', { - versionId: version.id, - succeeded: false, - statusMessage: 'this is the status' + assert.dom('.test-retry-build').doesNotExist('"retry" button removed after retrying'); }); - server.db.versions.update(version, { testResultId: testResult.id }); - await login(); - await visit(`/admin/build-results/${testResult.id}`); + test('detail page does not have a "retry" button for successful builds', async function(assert) { + let version = server.create('version'); + let testResult = server.create('testResult', { + version, + succeeded: true + }); - assert.dom('.test-build-status').hasText('this is the status', 'displays status message for the build'); - }); + await login(); + await visit(`/admin/build-results/${testResult.id}`); - test('detail page has a "retry" button for failed builds', async function(assert) { - assert.expect(3); - - let version = server.create('version'); - let testResult = server.create('testResult', { - versionId: version.id, - succeeded: false, - statusMessage: 'failed' + assert.dom('.test-retry-build').doesNotExist('no "retry" button should be displayed'); }); - server.db.versions.update(version, { testResultId: testResult.id }); - server.post('/test_results/:id/retry', function() { - assert.ok(true, 'makes retry request'); + test('when test result has ember-try results, displays a summary table', async function(assert) { + let testResult = server.create('testResult', { + version: server.create('version'), + succeeded: true, + emberTryResults: { + scenarios: [ + emberTryScenario('3.4'), + emberTryScenario('3.8'), + emberTryScenario('3.12'), + emberTryScenario('3.13') + ] + } + }); + + await login(); + await visit(`/admin/build-results/${testResult.id}`); + + assert.dom('[data-test-results-table]').exists(); }); - await login(); - await visit(`/admin/build-results/${testResult.id}`); - - assert.dom('.test-retry-build').exists('"retry" button exists'); + test('when test result does not have ember-try results, does not display summary table', async function(assert) { + let testResult = server.create('testResult', { + version: server.create('version'), + succeeded: true, + emberTryResults: null + }); - await click('.test-retry-build'); + await visit(`/admin/build-results/${testResult.id}`); - assert.dom('.test-retry-build').doesNotExist('"retry" button removed after retrying'); - }); - - test('detail page does not have a "retry" button for successful builds', async function(assert) { - let version = server.create('version'); - let testResult = server.create('testResult', { - versionId: version.id, - succeeded: true + assert.dom('[data-test-results-table]').doesNotExist(); }); - server.db.versions.update(version, { testResultId: testResult.id }); - - await login(); - await visit(`/admin/build-results/${testResult.id}`); - - assert.dom('.test-retry-build').doesNotExist('no "retry" button should be displayed'); }); }); diff --git a/tests/helpers/ember-try-scenario.js b/tests/helpers/ember-try-scenario.js new file mode 100644 index 00000000..56fdaf63 --- /dev/null +++ b/tests/helpers/ember-try-scenario.js @@ -0,0 +1,15 @@ +export default function emberTryScenario(version) { + return { + scenarioName: `ember-${version}`, + passed: true, + allowedToFail: false, + dependencies: [ + { + name: 'ember-source', + versionSeen: `${version}.2`, + versionExpected: `${version}.0`, + type: 'yarn' + } + ] + }; +} diff --git a/tests/integration/components/build-result-summary-table-test.js b/tests/integration/components/build-result-summary-table-test.js new file mode 100644 index 00000000..eed381e1 --- /dev/null +++ b/tests/integration/components/build-result-summary-table-test.js @@ -0,0 +1,60 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; + +import emberTryScenario from 'ember-observer/tests/helpers/ember-try-scenario'; + +module('Integration | Component | build-result-summary-table', function(hooks) { + setupRenderingTest(hooks); + + test('displays a row for each scenario in the provided results', async function(assert) { + this.emberTryResults = { + scenarios: [ + emberTryScenario('3.4'), + emberTryScenario('3.8'), + emberTryScenario('3.12'), + emberTryScenario('3.13'), + ] + }; + + await render(hbs` + + `); + + assert.dom('table tbody tr').exists({ count: 4 }); + }); + + test('displays information for a scenario', async function(assert) { + let scenario = emberTryScenario('3.13'); + this.emberTryResults = { + scenarios: [ + scenario + ] + }; + + await render(hbs` + + `); + + assert.dom('tbody tr td:nth-child(1)').hasText(scenario.scenarioName, 'displays scenario name in first column'); + assert.dom('tbody tr td:nth-child(2)').hasText('Passed', 'displays result in second column'); + }); + + test('when an allowed-to-fail scenario fails, result text reflects that', async function(assert) { + let scenario = emberTryScenario('3.13'); + scenario.allowedToFail = true; + scenario.passed = false; + this.emberTryResults = { + scenarios: [ + scenario + ] + }; + + await render(hbs` + + `); + + assert.dom('tbody tr td:nth-child(2)').hasText('Failed (allowed)'); + }); +});