diff --git a/.eslintrc.yml b/.eslintrc.yml index 2eb52b59deaa..cd3cd82d4e7a 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -100,6 +100,7 @@ overrides: - 'scripts/**/*' - '*.config.js' - '*.config.*.js' + - 'jest_resolver.js' - storybook/config/*.js rules: '@gitlab/require-i18n-strings': off diff --git a/app/assets/javascripts/related_issues/components/add_issuable_form.vue b/app/assets/javascripts/related_issues/components/add_issuable_form.vue index a9b5942efa97..4deb93e4b30c 100644 --- a/app/assets/javascripts/related_issues/components/add_issuable_form.vue +++ b/app/assets/javascripts/related_issues/components/add_issuable_form.vue @@ -197,7 +197,7 @@ export default {

{{ addRelatedErrorMessage }}

-
+
-
-
+
+
diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue index ad4bfe5b665f..4a255a3b916d 100644 --- a/app/assets/javascripts/sidebar/components/participants/participants.vue +++ b/app/assets/javascripts/sidebar/components/participants/participants.vue @@ -104,11 +104,11 @@ export default { {{ participantLabel }}
-
+
-
-
- {{ s__('TimeTracking|Spent') }} +
+
+ {{ s__('TimeTracking|Spent') }} {{ timeSpentHumanReadable }}
-
- {{ s__('TimeTrackingEstimated|Est') }} +
+ {{ s__('TimeTrackingEstimated|Est') }} {{ timeEstimateHumanReadable }}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 099dfa28b9f1..c0e297d8554d 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -759,7 +759,6 @@ $help-shortcut-header-color: #333; */ $issues-today-bg: #f3fff2 !default; $issues-today-border: #e1e8d5 !default; -$compare-display-color: #888; /* * Label diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index d8fc94cce0c5..c4df2e102bd3 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -220,21 +220,12 @@ } .cross-project-reference { - color: inherit; - span { - white-space: nowrap; width: 85%; - overflow: hidden; - position: relative; - display: inline-block; - text-overflow: ellipsis; } button { - float: right; padding: 1px 5px; - background-color: $gray-light; } } @@ -563,35 +554,17 @@ } } -.participants-list { - display: flex; - flex-wrap: wrap; -} - -.user-list { - display: flex; - flex-wrap: wrap; -} - .participants-author { - display: inline-block; - padding: 0 $gl-padding-8 $gl-padding-8 0; - &:nth-of-type(7n) { padding-right: 0; } - .author-link { - display: block; - } - .avatar.avatar-inline { margin: 0; } } .user-item { - display: inline-block; padding: 5px; flex-basis: 20%; @@ -803,10 +776,6 @@ } } -.add-issuable-form-actions { - margin-top: $gl-padding; -} - .time-tracker { .sidebar-collapsed-icon { > .stopwatch-svg { @@ -839,18 +808,7 @@ } .compare-display-container { - display: flex; - justify-content: space-between; - margin-top: 5px; - - .compare-display { - font-size: 13px; - color: $compare-display-color; - - .compare-value { - color: $gl-text-color; - } - } + font-size: 13px; } .time-tracking-help-state { diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml index 56b2b0d5801e..8ed40f74c885 100644 --- a/app/views/shared/milestones/_sidebar.html.haml +++ b/app/views/shared/milestones/_sidebar.html.haml @@ -164,8 +164,8 @@ .sidebar-collapsed-icon.dont-change-state = clipboard_button(text: milestone_ref, title: s_('MilestoneSidebar|Copy reference'), placement: "left", boundary: 'viewport') .cross-project-reference.hide-collapsed - %span + %span.gl-display-inline-block.gl-text-truncate = s_('MilestoneSidebar|Reference:') %span{ title: milestone_ref } = milestone_ref - = clipboard_button(text: milestone_ref, title: s_('MilestoneSidebar|Copy reference'), placement: "left", boundary: 'viewport') + = clipboard_button(text: milestone_ref, title: s_('MilestoneSidebar|Copy reference'), placement: "left", boundary: 'viewport', class: 'btn-clipboard btn-transparent gl-float-right gl-bg-gray-10') diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md index 0417dbeeede1..2708159979e2 100644 --- a/doc/development/testing_guide/frontend_testing.md +++ b/doc/development/testing_guide/frontend_testing.md @@ -783,20 +783,25 @@ often using fixtures to validate correct integration with the backend code. ### Use fixtures -Jest uses `spec/frontend/__helpers__/fixtures.js` to import fixtures in tests. - -The following are examples of tests that work for Jest: +To import a JSON fixture, `import` it using the `test_fixtures` alias. ```javascript +import responseBody from 'test_fixtures/some/fixture.json' // loads spec/frontend/fixtures/some/fixture.json + it('makes a request', () => { - const responseBody = getJSONFixture('some/fixture.json'); // loads spec/frontend/fixtures/some/fixture.json axiosMock.onGet(endpoint).reply(200, responseBody); myButton.click(); // ... }); +``` +For other fixtures, Jest uses `spec/frontend/__helpers__/fixtures.js` to import them in tests. + +The following are examples of tests that work for Jest: + +```javascript it('uses some HTML element', () => { loadFixtures('some/page.html'); // loads spec/frontend/fixtures/some/page.html and adds it to the DOM @@ -860,7 +865,7 @@ end This will create a new fixture located at `tmp/tests/frontend/fixtures-ee/graphql/releases/graphql/queries/all_releases.query.graphql.json`. -You can import the JSON fixture in a Jest test using the `getJSONFixture` method +You can import the JSON fixture in a Jest test using the `test_fixtures` alias [as described below](#use-fixtures). ## Data-driven tests diff --git a/jest.config.base.js b/jest.config.base.js index e0d5afbdbc97..7cf1136a0732 100644 --- a/jest.config.base.js +++ b/jest.config.base.js @@ -112,6 +112,7 @@ module.exports = (path, options = {}) => { cacheDirectory: '/tmp/cache/jest', modulePathIgnorePatterns: ['/.yarn-cache/'], reporters, + resolver: './jest_resolver.js', setupFilesAfterEnv: [`/${path}/test_setup.js`, 'jest-canvas-mock'], restoreMocks: true, transform: { diff --git a/jest_resolver.js b/jest_resolver.js new file mode 100644 index 000000000000..6cbc1b9f18fe --- /dev/null +++ b/jest_resolver.js @@ -0,0 +1,17 @@ +const fs = require('fs'); + +// Wrap jest default resolver to detect missing frontend fixtures. +module.exports = (request, options) => { + try { + return options.defaultResolver(request, options); + } catch (e) { + if (request.match(/tmp\/tests\/frontend\/fixtures/) && !fs.existsSync(request)) { + console.error( + '\x1b[1m\x1b[41m\x1b[30m %s \x1b[0m %s', + '!', + `Fixture file ${request} does not exist. Did you run bin/rake frontend:fixtures?`, + ); + } + throw e; + } +}; diff --git a/spec/frontend/.eslintrc.yml b/spec/frontend/.eslintrc.yml index 145e6c8961ac..2686a14f9eb3 100644 --- a/spec/frontend/.eslintrc.yml +++ b/spec/frontend/.eslintrc.yml @@ -26,4 +26,9 @@ rules: - off "@gitlab/no-global-event-off": - off - + import/no-unresolved: + - error + # The test fixtures and graphql schema are dynamically generated in CI + # during the `frontend-fixtures` and `graphql-schema-dump` jobs. + # They may not be present during linting. + - ignore: ['^test_fixtures\/', 'tmp/tests/graphql/gitlab_schema.graphql'] diff --git a/spec/frontend/deploy_freeze/helpers.js b/spec/frontend/deploy_freeze/helpers.js index 598f14d45f64..43e66183ab54 100644 --- a/spec/frontend/deploy_freeze/helpers.js +++ b/spec/frontend/deploy_freeze/helpers.js @@ -1,7 +1,8 @@ +import freezePeriodsFixture from 'test_fixtures/api/freeze-periods/freeze_periods.json'; +import timezoneDataFixture from 'test_fixtures/timezones/short.json'; import { secondsToHours } from '~/lib/utils/datetime_utility'; -export const freezePeriodsFixture = getJSONFixture('/api/freeze-periods/freeze_periods.json'); -export const timezoneDataFixture = getJSONFixture('/timezones/short.json'); +export { freezePeriodsFixture, timezoneDataFixture }; export const findTzByName = (identifier = '') => timezoneDataFixture.find(({ name }) => name.toLowerCase() === identifier.toLowerCase()); diff --git a/spec/frontend/sidebar/assignees_spec.js b/spec/frontend/sidebar/assignees_spec.js index be27a800418c..b3a67f18f82c 100644 --- a/spec/frontend/sidebar/assignees_spec.js +++ b/spec/frontend/sidebar/assignees_spec.js @@ -3,6 +3,7 @@ import { mount } from '@vue/test-utils'; import { trimText } from 'helpers/text_helper'; import UsersMockHelper from 'helpers/user_mock_data_helper'; import Assignee from '~/sidebar/components/assignees/assignees.vue'; +import AssigneeAvatarLink from '~/sidebar/components/assignees/assignee_avatar_link.vue'; import UsersMock from './mock_data'; describe('Assignee component', () => { @@ -19,6 +20,7 @@ describe('Assignee component', () => { }); }; + const findAllAvatarLinks = () => wrapper.findAllComponents(AssigneeAvatarLink); const findComponentTextNoUsers = () => wrapper.find('[data-testid="no-value"]'); const findCollapsedChildren = () => wrapper.findAll('.sidebar-collapsed-icon > *'); @@ -148,7 +150,7 @@ describe('Assignee component', () => { editable: true, }); - expect(wrapper.findAll('.user-item').length).toBe(users.length); + expect(findAllAvatarLinks()).toHaveLength(users.length); expect(wrapper.find('.user-list-more').exists()).toBe(false); }); @@ -178,9 +180,9 @@ describe('Assignee component', () => { users, }); - const userItems = wrapper.findAll('.user-list .user-item a'); + const userItems = findAllAvatarLinks(); - expect(userItems.length).toBe(3); + expect(userItems).toHaveLength(3); expect(userItems.at(0).attributes('title')).toBe(users[2].name); }); diff --git a/spec/frontend_integration/test_helpers/mock_server/graphql.js b/spec/frontend_integration/test_helpers/mock_server/graphql.js index 27396842523e..d4ee7c02839e 100644 --- a/spec/frontend_integration/test_helpers/mock_server/graphql.js +++ b/spec/frontend_integration/test_helpers/mock_server/graphql.js @@ -1,9 +1,7 @@ import { buildSchema, graphql } from 'graphql'; import { memoize } from 'lodash'; -// The graphql schema is dynamically generated in CI -// during the `graphql-schema-dump` job. -// eslint-disable-next-line global-require, import/no-unresolved +// eslint-disable-next-line global-require const getGraphqlSchema = () => require('../../../../tmp/tests/graphql/gitlab_schema.graphql'); const graphqlResolvers = {