Commit 00112e67 authored by Mike Greiling's avatar Mike Greiling

Merge branch 'ps-ide-defer-loading-terminal-modules' into 'master'

Chunk deferred runtime and terminal modules in Web IDE

See merge request gitlab-org/gitlab!47719
parents 9169c89a c3f24fcb
<script> <script>
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { import {
WEBIDE_MARK_APP_START, WEBIDE_MARK_APP_START,
...@@ -29,15 +30,17 @@ export default { ...@@ -29,15 +30,17 @@ export default {
components: { components: {
IdeSidebar, IdeSidebar,
RepoEditor, RepoEditor,
'error-message': () => import('./error_message.vue'), GlButton,
'gl-button': () => import('@gitlab/ui/src/components/base/button/button.vue'), GlLoadingIcon,
'gl-loading-icon': () => import('@gitlab/ui/src/components/base/loading_icon/loading_icon.vue'), ErrorMessage: () => import(/* webpackChunkName: 'ide_runtime' */ './error_message.vue'),
'commit-editor-header': () => import('./commit_sidebar/editor_header.vue'), CommitEditorHeader: () =>
'repo-tabs': () => import('./repo_tabs.vue'), import(/* webpackChunkName: 'ide_runtime' */ './commit_sidebar/editor_header.vue'),
'ide-status-bar': () => import('./ide_status_bar.vue'), RepoTabs: () => import(/* webpackChunkName: 'ide_runtime' */ './repo_tabs.vue'),
'find-file': () => import('~/vue_shared/components/file_finder/index.vue'), IdeStatusBar: () => import(/* webpackChunkName: 'ide_runtime' */ './ide_status_bar.vue'),
'right-pane': () => import('./panes/right.vue'), FindFile: () =>
'new-modal': () => import('./new_dropdown/modal.vue'), import(/* webpackChunkName: 'ide_runtime' */ '~/vue_shared/components/file_finder/index.vue'),
RightPane: () => import(/* webpackChunkName: 'ide_runtime' */ './panes/right.vue'),
NewModal: () => import(/* webpackChunkName: 'ide_runtime' */ './new_dropdown/modal.vue'),
}, },
mixins: [glFeatureFlagsMixin()], mixins: [glFeatureFlagsMixin()],
data() { data() {
......
...@@ -14,8 +14,10 @@ export default { ...@@ -14,8 +14,10 @@ export default {
ResizablePanel, ResizablePanel,
ActivityBar, ActivityBar,
IdeTree, IdeTree,
[leftSidebarViews.review.name]: () => import('./ide_review.vue'), [leftSidebarViews.review.name]: () =>
[leftSidebarViews.commit.name]: () => import('./repo_commit_section.vue'), import(/* webpackChunkName: 'ide_runtime' */ './ide_review.vue'),
[leftSidebarViews.commit.name]: () =>
import(/* webpackChunkName: 'ide_runtime' */ './repo_commit_section.vue'),
CommitForm, CommitForm,
IdeProjectHeader, IdeProjectHeader,
}, },
......
<script> <script>
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import EmptyState from './empty_state.vue'; import EmptyState from './empty_state.vue';
import TerminalSession from './session.vue';
export default { export default {
components: { components: {
EmptyState, EmptyState,
TerminalSession, TerminalSession: () => import(/* webpackChunkName: 'ide_terminal' */ './session.vue'),
}, },
computed: { computed: {
...mapState('terminal', ['isShowSplash', 'paths']), ...mapState('terminal', ['isShowSplash', 'paths']),
......
...@@ -104,37 +104,6 @@ describe('Component', () => { ...@@ -104,37 +104,6 @@ describe('Component', () => {
Remember that the performance of each test depends on the environment. Remember that the performance of each test depends on the environment.
### Timout error due to async components
If your component is fetching some other components asynchroneously based on some conditions, it might happen so that your Jest suite for this component will become flaky timing out from time to time.
```javascript
// ide.vue
export default {
components: {
'error-message': () => import('./error_message.vue'),
'gl-button': () => import('@gitlab/ui/src/components/base/button/button.vue'),
...
};
```
To address this issue, you can "help" Jest by stubbing the async components so that Jest would not need to fetch those asynchroneously at the run-time.
```javascript
// ide_spec.js
import { GlButton } from '@gitlab/ui';
import ErrorMessage from '~/ide/components/error_message.vue';
...
return shallowMount(ide, {
...
stubs: {
ErrorMessage,
GlButton,
...
},
})
```
## What and how to test ## What and how to test
Before jumping into more gritty details about Jest-specific workflows like mocks and spies, we should briefly cover what to test with Jest. Before jumping into more gritty details about Jest-specific workflows like mocks and spies, we should briefly cover what to test with Jest.
......
import Vuex from 'vuex'; import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlButton, GlLoadingIcon } from '@gitlab/ui'; import waitForPromises from 'helpers/wait_for_promises';
import { createStore } from '~/ide/stores'; import { createStore } from '~/ide/stores';
import ErrorMessage from '~/ide/components/error_message.vue'; import ErrorMessage from '~/ide/components/error_message.vue';
import FindFile from '~/vue_shared/components/file_finder/index.vue';
import CommitEditorHeader from '~/ide/components/commit_sidebar/editor_header.vue';
import RepoTabs from '~/ide/components/repo_tabs.vue';
import IdeStatusBar from '~/ide/components/ide_status_bar.vue';
import RightPane from '~/ide/components/panes/right.vue';
import NewModal from '~/ide/components/new_dropdown/modal.vue';
import ide from '~/ide/components/ide.vue'; import ide from '~/ide/components/ide.vue';
import { file } from '../helpers'; import { file } from '../helpers';
import { projectData } from '../mock_data'; import { projectData } from '../mock_data';
...@@ -39,17 +32,6 @@ describe('WebIDE', () => { ...@@ -39,17 +32,6 @@ describe('WebIDE', () => {
return shallowMount(ide, { return shallowMount(ide, {
store, store,
localVue, localVue,
stubs: {
ErrorMessage,
GlButton,
GlLoadingIcon,
CommitEditorHeader,
RepoTabs,
IdeStatusBar,
FindFile,
RightPane,
NewModal,
},
}); });
} }
...@@ -74,27 +56,24 @@ describe('WebIDE', () => { ...@@ -74,27 +56,24 @@ describe('WebIDE', () => {
describe('ide component, non-empty repo', () => { describe('ide component, non-empty repo', () => {
describe('error message', () => { describe('error message', () => {
it('does not show error message when it is not set', () => { it.each`
errorMessage | exists
${null} | ${false}
${{ text: 'error' }} | ${true}
`(
'should error message exists=$exists when errorMessage=$errorMessage',
async ({ errorMessage, exists }) => {
wrapper = createComponent({ wrapper = createComponent({
state: { state: {
errorMessage: null, errorMessage,
}, },
}); });
expect(wrapper.find(ErrorMessage).exists()).toBe(false); await waitForPromises();
});
it('shows error message when set', () => { expect(wrapper.find(ErrorMessage).exists()).toBe(exists);
wrapper = createComponent({
state: {
errorMessage: {
text: 'error',
}, },
}, );
});
expect(wrapper.find(ErrorMessage).exists()).toBe(true);
});
}); });
describe('onBeforeUnload', () => { describe('onBeforeUnload', () => {
......
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { TEST_HOST } from 'spec/test_constants'; import { TEST_HOST } from 'spec/test_constants';
import waitForPromises from 'helpers/wait_for_promises';
import TerminalEmptyState from '~/ide/components/terminal/empty_state.vue'; import TerminalEmptyState from '~/ide/components/terminal/empty_state.vue';
import TerminalView from '~/ide/components/terminal/view.vue'; import TerminalView from '~/ide/components/terminal/view.vue';
import TerminalSession from '~/ide/components/terminal/session.vue'; import TerminalSession from '~/ide/components/terminal/session.vue';
...@@ -17,7 +18,7 @@ describe('IDE TerminalView', () => { ...@@ -17,7 +18,7 @@ describe('IDE TerminalView', () => {
let getters; let getters;
let wrapper; let wrapper;
const factory = () => { const factory = async () => {
const store = new Vuex.Store({ const store = new Vuex.Store({
modules: { modules: {
terminal: { terminal: {
...@@ -30,6 +31,9 @@ describe('IDE TerminalView', () => { ...@@ -30,6 +31,9 @@ describe('IDE TerminalView', () => {
}); });
wrapper = shallowMount(TerminalView, { localVue, store }); wrapper = shallowMount(TerminalView, { localVue, store });
// Uses deferred components, so wait for those to load...
await waitForPromises();
}; };
beforeEach(() => { beforeEach(() => {
...@@ -59,8 +63,8 @@ describe('IDE TerminalView', () => { ...@@ -59,8 +63,8 @@ describe('IDE TerminalView', () => {
wrapper.destroy(); wrapper.destroy();
}); });
it('renders empty state', () => { it('renders empty state', async () => {
factory(); await factory();
expect(wrapper.find(TerminalEmptyState).props()).toEqual({ expect(wrapper.find(TerminalEmptyState).props()).toEqual({
helpPath: TEST_HELP_PATH, helpPath: TEST_HELP_PATH,
...@@ -69,8 +73,8 @@ describe('IDE TerminalView', () => { ...@@ -69,8 +73,8 @@ describe('IDE TerminalView', () => {
}); });
}); });
it('hides splash and starts, when started', () => { it('hides splash and starts, when started', async () => {
factory(); await factory();
expect(actions.startSession).not.toHaveBeenCalled(); expect(actions.startSession).not.toHaveBeenCalled();
expect(actions.hideSplash).not.toHaveBeenCalled(); expect(actions.hideSplash).not.toHaveBeenCalled();
...@@ -81,9 +85,9 @@ describe('IDE TerminalView', () => { ...@@ -81,9 +85,9 @@ describe('IDE TerminalView', () => {
expect(actions.hideSplash).toHaveBeenCalled(); expect(actions.hideSplash).toHaveBeenCalled();
}); });
it('shows Web Terminal when started', () => { it('shows Web Terminal when started', async () => {
state.isShowSplash = false; state.isShowSplash = false;
factory(); await factory();
expect(wrapper.find(TerminalEmptyState).exists()).toBe(false); expect(wrapper.find(TerminalEmptyState).exists()).toBe(false);
expect(wrapper.find(TerminalSession).exists()).toBe(true); expect(wrapper.find(TerminalSession).exists()).toBe(true);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment