Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ce5c259086
commit
b0e1e54ce9
30 changed files with 403 additions and 39 deletions
|
@ -8,6 +8,7 @@ import { parseBoolean } from '../lib/utils/common_utils';
|
|||
import { resetServiceWorkersPublicPath } from '../lib/utils/webpack';
|
||||
import ide from './components/ide.vue';
|
||||
import { createRouter } from './ide_router';
|
||||
import { initGitlabWebIDE } from './init_gitlab_web_ide';
|
||||
import { DEFAULT_THEME } from './lib/themes';
|
||||
import { createStore } from './stores';
|
||||
|
||||
|
@ -34,7 +35,7 @@ Vue.use(PerformancePlugin, {
|
|||
* @param {extendStoreCallback} options.extendStore -
|
||||
* Function that receives the default store and returns an extended one.
|
||||
*/
|
||||
export const initIde = (el, options = {}) => {
|
||||
export const initLegacyWebIDE = (el, options = {}) => {
|
||||
if (!el) return null;
|
||||
|
||||
const { rootComponent = ide, extendStore = identity } = options;
|
||||
|
@ -93,8 +94,15 @@ export const initIde = (el, options = {}) => {
|
|||
*/
|
||||
export function startIde(options) {
|
||||
const ideElement = document.getElementById('ide');
|
||||
if (ideElement) {
|
||||
|
||||
if (!ideElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gon.features?.vscodeWebIde) {
|
||||
initGitlabWebIDE(ideElement);
|
||||
} else {
|
||||
resetServiceWorkersPublicPath();
|
||||
initIde(ideElement, options);
|
||||
initLegacyWebIDE(ideElement, options);
|
||||
}
|
||||
}
|
||||
|
|
30
app/assets/javascripts/ide/init_gitlab_web_ide.js
Normal file
30
app/assets/javascripts/ide/init_gitlab_web_ide.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { cleanTrailingSlash } from './stores/utils';
|
||||
|
||||
export const initGitlabWebIDE = async (el) => {
|
||||
const { start } = await import('@gitlab/web-ide');
|
||||
|
||||
const { gitlab_url: gitlabUrl } = window.gon;
|
||||
const baseUrl = new URL(process.env.GITLAB_WEB_IDE_PUBLIC_PATH, window.location.origin);
|
||||
|
||||
// what: Pull what we need from the element. We will replace it soon.
|
||||
const { path_with_namespace: projectPath } = JSON.parse(el.dataset.project);
|
||||
const { cspNonce: nonce, branchName: ref } = el.dataset;
|
||||
|
||||
// what: Clean up the element, but preserve id.
|
||||
// why: This way we don't inherit any `ide-loading` side-effects. This
|
||||
// mirrors the behavior of Vue when it mounts to an element.
|
||||
const newEl = document.createElement(el.tagName);
|
||||
newEl.id = el.id;
|
||||
newEl.classList.add('gl--flex-center', 'gl-relative', 'gl-h-full');
|
||||
|
||||
el.replaceWith(newEl);
|
||||
|
||||
// what: Trigger start on our new mounting element
|
||||
await start(newEl, {
|
||||
baseUrl: cleanTrailingSlash(baseUrl.href),
|
||||
projectPath,
|
||||
gitlabUrl,
|
||||
ref,
|
||||
nonce,
|
||||
});
|
||||
};
|
|
@ -27,7 +27,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
isUpdating: false,
|
||||
isFocused: false,
|
||||
yamlEditorExtension: null,
|
||||
};
|
||||
},
|
||||
|
@ -60,19 +60,23 @@ export default {
|
|||
this.editor.onDidChangeModelContent(
|
||||
debounce(() => this.handleChange(), CONTENT_UPDATE_DEBOUNCE),
|
||||
);
|
||||
this.editor.onDidFocusEditorText(() => {
|
||||
this.isFocused = true;
|
||||
});
|
||||
this.editor.onDidBlurEditorText(() => {
|
||||
this.isFocused = false;
|
||||
});
|
||||
this.updateEditorContent();
|
||||
this.emitValue();
|
||||
},
|
||||
methods: {
|
||||
async updateEditorContent() {
|
||||
this.isUpdating = true;
|
||||
this.editor.setDoc(this.doc);
|
||||
this.isUpdating = false;
|
||||
this.requestHighlight(this.highlight);
|
||||
},
|
||||
handleChange() {
|
||||
this.emitValue();
|
||||
if (!this.isUpdating) {
|
||||
if (this.isFocused) {
|
||||
this.handleTouch();
|
||||
}
|
||||
},
|
||||
|
|
|
@ -5,6 +5,7 @@ import { uniqueId } from 'lodash';
|
|||
import { merge } from '~/lib/utils/yaml';
|
||||
import { __ } from '~/locale';
|
||||
import { isValidStepSeq } from '~/pipeline_wizard/validators';
|
||||
import Tracking from '~/tracking';
|
||||
import YamlEditor from './editor.vue';
|
||||
import WizardStep from './step.vue';
|
||||
import CommitStep from './commit.vue';
|
||||
|
@ -16,6 +17,8 @@ export const i18n = {
|
|||
YAML-file for you to add to your repository`),
|
||||
};
|
||||
|
||||
const trackingMixin = Tracking.mixin();
|
||||
|
||||
export default {
|
||||
name: 'PipelineWizardWrapper',
|
||||
i18n,
|
||||
|
@ -25,6 +28,7 @@ export default {
|
|||
WizardStep,
|
||||
CommitStep,
|
||||
},
|
||||
mixins: [trackingMixin],
|
||||
props: {
|
||||
steps: {
|
||||
type: Object,
|
||||
|
@ -43,6 +47,11 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
templateId: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -77,6 +86,11 @@ export default {
|
|||
template: this.steps.get(i).get('template', true),
|
||||
}));
|
||||
},
|
||||
tracking() {
|
||||
return {
|
||||
category: `pipeline_wizard:${this.templateId}`,
|
||||
};
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isLastStep(value) {
|
||||
|
@ -84,9 +98,6 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
getStep(index) {
|
||||
return this.steps.get(index);
|
||||
},
|
||||
resetHighlight() {
|
||||
this.highlightPath = null;
|
||||
},
|
||||
|
@ -106,6 +117,43 @@ export default {
|
|||
});
|
||||
return doc;
|
||||
},
|
||||
onBack() {
|
||||
this.currentStepIndex -= 1;
|
||||
this.track('click_button', {
|
||||
property: 'back',
|
||||
label: 'pipeline_wizard_navigation',
|
||||
extras: {
|
||||
fromStep: this.currentStepIndex + 1,
|
||||
toStep: this.currentStepIndex,
|
||||
},
|
||||
});
|
||||
},
|
||||
onNext() {
|
||||
this.currentStepIndex += 1;
|
||||
this.track('click_button', {
|
||||
property: 'next',
|
||||
label: 'pipeline_wizard_navigation',
|
||||
extras: {
|
||||
fromStep: this.currentStepIndex - 1,
|
||||
toStep: this.currentStepIndex,
|
||||
},
|
||||
});
|
||||
},
|
||||
onDone() {
|
||||
this.$emit('done');
|
||||
this.track('click_button', {
|
||||
label: 'pipeline_wizard_commit',
|
||||
property: 'commit',
|
||||
});
|
||||
},
|
||||
onEditorTouched() {
|
||||
this.track('edit', {
|
||||
label: 'pipeline_wizard_editor_interaction',
|
||||
extras: {
|
||||
currentStep: this.currentStepIndex,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -127,8 +175,8 @@ export default {
|
|||
:file-content="pipelineBlob"
|
||||
:filename="filename"
|
||||
:project-path="projectPath"
|
||||
@back="currentStepIndex--"
|
||||
@done="$emit('done')"
|
||||
@back="onBack"
|
||||
@done="onDone"
|
||||
/>
|
||||
<wizard-step
|
||||
v-for="(step, i) in stepList"
|
||||
|
@ -141,8 +189,8 @@ export default {
|
|||
:highlight.sync="highlightPath"
|
||||
:inputs="step.inputs"
|
||||
:template="step.template"
|
||||
@back="currentStepIndex--"
|
||||
@next="currentStepIndex++"
|
||||
@back="onBack"
|
||||
@next="onNext"
|
||||
@update:compiled="onUpdate"
|
||||
/>
|
||||
</section>
|
||||
|
@ -162,6 +210,7 @@ export default {
|
|||
:highlight="highlightPath"
|
||||
class="gl-w-full"
|
||||
@update:yaml="onEditorUpdate"
|
||||
@touch.once="onEditorTouched"
|
||||
/>
|
||||
<div
|
||||
v-if="showPlaceholder"
|
||||
|
|
|
@ -42,6 +42,9 @@ export default {
|
|||
steps() {
|
||||
return this.parsedTemplate?.get('steps');
|
||||
},
|
||||
templateId() {
|
||||
return this.parsedTemplate?.get('id');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -60,6 +63,7 @@ export default {
|
|||
:filename="filename"
|
||||
:project-path="projectPath"
|
||||
:steps="steps"
|
||||
:template-id="templateId"
|
||||
@done="$emit('done')"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
id: gitlab/pages
|
||||
title: Get started with Pages
|
||||
description: "GitLab Pages lets you deploy static websites in minutes. All you
|
||||
need is a .gitlab-ci.yml file. Follow the below steps to
|
||||
|
|
|
@ -13,6 +13,7 @@ class IdeController < ApplicationController
|
|||
push_frontend_feature_flag(:build_service_proxy)
|
||||
push_frontend_feature_flag(:schema_linting)
|
||||
push_frontend_feature_flag(:reject_unsigned_commits_by_gitlab)
|
||||
push_frontend_feature_flag(:vscode_web_ide, current_user)
|
||||
define_index_vars
|
||||
end
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@ module IdeHelper
|
|||
'web-terminal-svg-path' => image_path('illustrations/web-ide_promotion.svg'),
|
||||
'web-terminal-help-path' => help_page_path('user/project/web_ide/index.md', anchor: 'interactive-web-terminals-for-the-web-ide'),
|
||||
'web-terminal-config-help-path' => help_page_path('user/project/web_ide/index.md', anchor: 'web-ide-configuration-file'),
|
||||
'web-terminal-runners-help-path' => help_page_path('user/project/web_ide/index.md', anchor: 'runner-configuration')
|
||||
'web-terminal-runners-help-path' => help_page_path('user/project/web_ide/index.md', anchor: 'runner-configuration'),
|
||||
'csp-nonce' => content_security_policy_nonce
|
||||
}
|
||||
end
|
||||
|
||||
|
|
8
config/feature_flags/development/vscode_web_ide.yml
Normal file
8
config/feature_flags/development/vscode_web_ide.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: vscode_web_ide
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95169
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371084
|
||||
milestone: '15.4'
|
||||
type: development
|
||||
group: group::editor
|
||||
default_enabled: false
|
|
@ -6,6 +6,7 @@ const path = require('path');
|
|||
|
||||
const BABEL_VERSION = require('@babel/core/package.json').version;
|
||||
const SOURCEGRAPH_VERSION = require('@sourcegraph/code-host-integration/package.json').version;
|
||||
const GITLAB_WEB_IDE_VERSION = require('@gitlab/web-ide/package.json').version;
|
||||
|
||||
const BABEL_LOADER_VERSION = require('babel-loader/package.json').version;
|
||||
const CompressionPlugin = require('compression-webpack-plugin');
|
||||
|
@ -60,11 +61,16 @@ const NO_SOURCEMAPS = process.env.NO_SOURCEMAPS && process.env.NO_SOURCEMAPS !==
|
|||
const WEBPACK_OUTPUT_PATH = path.join(ROOT_PATH, 'public/assets/webpack');
|
||||
const WEBPACK_PUBLIC_PATH = '/assets/webpack/';
|
||||
const SOURCEGRAPH_PACKAGE = '@sourcegraph/code-host-integration';
|
||||
const GITLAB_WEB_IDE_PACKAGE = '@gitlab/web-ide';
|
||||
|
||||
const SOURCEGRAPH_PATH = path.join('sourcegraph', SOURCEGRAPH_VERSION, '/');
|
||||
const SOURCEGRAPH_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, SOURCEGRAPH_PATH);
|
||||
const SOURCEGRAPH_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, SOURCEGRAPH_PATH);
|
||||
|
||||
const GITLAB_WEB_IDE_PATH = path.join('gitlab-vscode', GITLAB_WEB_IDE_VERSION, '/');
|
||||
const GITLAB_WEB_IDE_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, GITLAB_WEB_IDE_PATH);
|
||||
const GITLAB_WEB_IDE_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, GITLAB_WEB_IDE_PATH);
|
||||
|
||||
const devtool = IS_PRODUCTION ? 'source-map' : 'cheap-module-eval-source-map';
|
||||
|
||||
let autoEntriesCount = 0;
|
||||
|
@ -613,6 +619,10 @@ module.exports = {
|
|||
ignore: ['package.json'],
|
||||
},
|
||||
},
|
||||
{
|
||||
from: path.join(ROOT_PATH, 'node_modules', GITLAB_WEB_IDE_PACKAGE, 'dist', 'public'),
|
||||
to: GITLAB_WEB_IDE_OUTPUT_PATH,
|
||||
},
|
||||
{
|
||||
from: path.join(
|
||||
ROOT_PATH,
|
||||
|
@ -720,6 +730,7 @@ module.exports = {
|
|||
IS_JH: IS_JH ? 'window.gon && window.gon.jh' : JSON.stringify(false),
|
||||
// This is used by Sourcegraph because these assets are loaded dnamically
|
||||
'process.env.SOURCEGRAPH_PUBLIC_PATH': JSON.stringify(SOURCEGRAPH_PUBLIC_PATH),
|
||||
'process.env.GITLAB_WEB_IDE_PUBLIC_PATH': JSON.stringify(GITLAB_WEB_IDE_PUBLIC_PATH),
|
||||
...(IS_PRODUCTION ? {} : { LIVE_RELOAD: DEV_SERVER_LIVERELOAD }),
|
||||
}),
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ consists of 2-3 steps, for a total of 3-4 steps visible to the user.
|
|||
|
||||
```yaml
|
||||
# ~/pipeline_wizard/templates/my_template.yml
|
||||
|
||||
id: gitlab/my-template
|
||||
title: Set up my specific tech pipeline
|
||||
description: Here's two or three introductory sentences that help the user understand what this wizard is going to set up.
|
||||
steps:
|
||||
|
@ -156,12 +156,13 @@ Webpack does not parse it as an Object.
|
|||
|
||||
In the root element of the template file, you can define the following properties:
|
||||
|
||||
| Name | Required | Type | Description |
|
||||
|---------------|------------------------|--------|---------------------------------------------------------------------------------------|
|
||||
| `title` | **{check-circle}** Yes | string | The page title as displayed to the user. It becomes an `h1` heading above the wizard. |
|
||||
| `description` | **{check-circle}** Yes | string | The page description as displayed to the user. |
|
||||
| `filename` | **{dotted-circle}** No | string | The name of the file that is being generated. Defaults to `.gitlab-ci.yml`. |
|
||||
| `steps` | **{check-circle}** Yes | list | A list of [step definitions](#step-reference). |
|
||||
| Name | Required | Type | Description |
|
||||
|---------------|------------------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | **{check-circle}** Yes | string | A unique template ID. This ID should follow a namespacing pattern, with a forward slash `/` as separator. Templates committed to GitLab source code should always begin with `gitlab`. For example: `gitlab/my-template` |
|
||||
| `title` | **{check-circle}** Yes | string | The page title as displayed to the user. It becomes an `h1` heading above the wizard. |
|
||||
| `description` | **{check-circle}** Yes | string | The page description as displayed to the user. |
|
||||
| `filename` | **{dotted-circle}** No | string | The name of the file that is being generated. Defaults to `.gitlab-ci.yml`. |
|
||||
| `steps` | **{check-circle}** Yes | list | A list of [step definitions](#step-reference). |
|
||||
|
||||
### `step` Reference
|
||||
|
||||
|
|
|
@ -326,15 +326,23 @@ Because a maintainer's job only depends on their knowledge of the overall GitLab
|
|||
codebase, and not that of any specific domain, they can review, approve, and merge
|
||||
merge requests from any team and in any product area.
|
||||
|
||||
Maintainers are the DRI of assuring that the acceptance criteria of a merge request are reasonably met.
|
||||
In general, [quality is everyone’s responsibility](https://about.gitlab.com/handbook/engineering/quality/),
|
||||
but maintainers of an MR are held responsible for **ensuring** that an MR meets those general quality standards.
|
||||
|
||||
If a maintainer feels that an MR is substantial enough, or requires a [domain expert](#domain-experts),
|
||||
maintainers have the discretion to request a review from another reviewer, or maintainer. Here are some
|
||||
examples of maintainers proactively doing this during review:
|
||||
|
||||
- <https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82708#note_872325561>
|
||||
- <https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38003#note_387981596>
|
||||
- <https://gitlab.com/gitlab-org/gitlab/-/merge_requests/14017#note_178828088>
|
||||
|
||||
Maintainers do their best to also review the specifics of the chosen solution
|
||||
before merging, but as they are not necessarily [domain experts](#domain-experts), they may be poorly
|
||||
placed to do so without an unreasonable investment of time. In those cases, they
|
||||
defer to the judgment of the author and earlier reviewers, in favor of focusing on their primary responsibilities.
|
||||
|
||||
If a maintainer feels that an MR is substantial enough that it warrants a review from a [domain expert](#domain-experts),
|
||||
and it is unclear whether a domain expert have been involved in the reviews to date,
|
||||
they may request a [domain expert's](#domain-experts) review before merging the MR.
|
||||
|
||||
If a developer who happens to also be a maintainer was involved in a merge request
|
||||
as a reviewer, it is recommended that they are not also picked as the maintainer to ultimately approve and merge it.
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
"@gitlab/svgs": "3.1.0",
|
||||
"@gitlab/ui": "43.7.1",
|
||||
"@gitlab/visual-review-tools": "1.7.3",
|
||||
"@gitlab/web-ide": "0.0.1-dev-20220815034418",
|
||||
"@rails/actioncable": "6.1.4-7",
|
||||
"@rails/ujs": "6.1.4-7",
|
||||
"@sentry/browser": "5.30.0",
|
||||
|
|
|
@ -9,6 +9,8 @@ RSpec.describe 'IDE user commits changes', :js do
|
|||
let(:user) { project.first_owner }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vscode_web_ide: false)
|
||||
|
||||
sign_in(user)
|
||||
|
||||
ide_visit(project)
|
||||
|
|
|
@ -8,6 +8,8 @@ RSpec.describe 'IDE merge request', :js do
|
|||
let(:user) { project.first_owner }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vscode_web_ide: false)
|
||||
|
||||
sign_in(user)
|
||||
|
||||
visit(merge_request_path(merge_request))
|
||||
|
|
|
@ -4,12 +4,14 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe 'IDE', :js do
|
||||
describe 'sub-groups' do
|
||||
let(:ide_iframe_selector) { '#ide iframe' }
|
||||
let(:user) { create(:user) }
|
||||
let(:group) { create(:group) }
|
||||
let(:subgroup) { create(:group, parent: group) }
|
||||
let(:subgroup_project) { create(:project, :repository, namespace: subgroup) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vscode_web_ide: vscode_ff)
|
||||
subgroup_project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
|
||||
|
@ -20,8 +22,28 @@ RSpec.describe 'IDE', :js do
|
|||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'loads project in web IDE' do
|
||||
expect(page).to have_selector('.context-header', text: subgroup_project.name)
|
||||
context 'with vscode feature flag on' do
|
||||
let(:vscode_ff) { true }
|
||||
|
||||
it 'loads project in Web IDE' do
|
||||
iframe = find(ide_iframe_selector)
|
||||
|
||||
page.within_frame(iframe) do
|
||||
expect(page).to have_selector('.title', text: subgroup_project.name.upcase)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with vscode feature flag off' do
|
||||
let(:vscode_ff) { false }
|
||||
|
||||
it 'loads project in legacy Web IDE' do
|
||||
expect(page).to have_selector('.context-header', text: subgroup_project.name)
|
||||
end
|
||||
|
||||
it 'does not load new Web IDE' do
|
||||
expect(page).not_to have_selector(ide_iframe_selector)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,10 @@ RSpec.describe 'User creates new blob', :js do
|
|||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :empty_repo) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vscode_web_ide: false)
|
||||
end
|
||||
|
||||
shared_examples 'creating a file' do
|
||||
it 'allows the user to add a new file in Web IDE' do
|
||||
visit project_path(project)
|
||||
|
|
|
@ -9,6 +9,8 @@ RSpec.describe 'Projects > Files > Project owner sees a link to create a license
|
|||
let(:project_maintainer) { project.first_owner }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vscode_web_ide: false)
|
||||
|
||||
sign_in(project_maintainer)
|
||||
end
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ RSpec.describe 'Projects > Files > User edits files', :js do
|
|||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vscode_web_ide: false)
|
||||
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ RSpec.describe 'Multi-file editor new directory', :js do
|
|||
let(:project) { create(:project, :repository) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vscode_web_ide: false)
|
||||
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ RSpec.describe 'Multi-file editor new file', :js do
|
|||
let(:project) { create(:project, :repository) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vscode_web_ide: false)
|
||||
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
|
||||
|
|
|
@ -117,6 +117,10 @@ RSpec.describe 'Projects tree', :js do
|
|||
end
|
||||
|
||||
context 'web IDE' do
|
||||
before do
|
||||
stub_feature_flags(vscode_web_ide: false)
|
||||
end
|
||||
|
||||
it 'opens folder in IDE' do
|
||||
visit project_tree_path(project, File.join('master', 'bar'))
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ RSpec.describe 'Multi-file editor upload file', :js do
|
|||
let(:img_file) { File.join(Rails.root, 'spec', 'fixtures', 'dk.png') }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vscode_web_ide: false)
|
||||
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
|
||||
|
|
62
spec/frontend/ide/init_gitlab_web_ide_spec.js
Normal file
62
spec/frontend/ide/init_gitlab_web_ide_spec.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
import { start } from '@gitlab/web-ide';
|
||||
import { initGitlabWebIDE } from '~/ide/init_gitlab_web_ide';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
|
||||
jest.mock('@gitlab/web-ide');
|
||||
|
||||
const ROOT_ELEMENT_ID = 'ide';
|
||||
const TEST_NONCE = 'test123nonce';
|
||||
const TEST_PROJECT = { path_with_namespace: 'group1/project1' };
|
||||
const TEST_BRANCH_NAME = '12345-foo-patch';
|
||||
const TEST_GITLAB_URL = 'https://test-gitlab/';
|
||||
const TEST_GITLAB_WEB_IDE_PUBLIC_PATH = 'test/webpack/assets/gitlab-web-ide/public/path';
|
||||
|
||||
describe('ide/init_gitlab_web_ide', () => {
|
||||
const createRootElement = () => {
|
||||
const el = document.createElement('div');
|
||||
|
||||
el.id = ROOT_ELEMENT_ID;
|
||||
// why: We'll test that this class is removed later
|
||||
el.classList.add('ide-loading');
|
||||
el.dataset.project = JSON.stringify(TEST_PROJECT);
|
||||
el.dataset.cspNonce = TEST_NONCE;
|
||||
el.dataset.branchName = TEST_BRANCH_NAME;
|
||||
|
||||
document.body.append(el);
|
||||
};
|
||||
const findRootElement = () => document.getElementById(ROOT_ELEMENT_ID);
|
||||
const act = () => initGitlabWebIDE(findRootElement());
|
||||
|
||||
beforeEach(() => {
|
||||
process.env.GITLAB_WEB_IDE_PUBLIC_PATH = TEST_GITLAB_WEB_IDE_PUBLIC_PATH;
|
||||
window.gon.gitlab_url = TEST_GITLAB_URL;
|
||||
|
||||
createRootElement();
|
||||
|
||||
act();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
it('calls start with element', () => {
|
||||
expect(start).toHaveBeenCalledWith(findRootElement(), {
|
||||
baseUrl: `${TEST_HOST}/${TEST_GITLAB_WEB_IDE_PUBLIC_PATH}`,
|
||||
projectPath: TEST_PROJECT.path_with_namespace,
|
||||
ref: TEST_BRANCH_NAME,
|
||||
gitlabUrl: TEST_GITLAB_URL,
|
||||
nonce: TEST_NONCE,
|
||||
});
|
||||
});
|
||||
|
||||
it('clears classes and data from root element', () => {
|
||||
const rootEl = findRootElement();
|
||||
|
||||
// why: Snapshot to test that `ide-loading` was removed and no other
|
||||
// artifacts are remaining.
|
||||
expect(rootEl.outerHTML).toBe(
|
||||
'<div id="ide" class="gl--flex-center gl-relative gl-h-full"></div>',
|
||||
);
|
||||
});
|
||||
});
|
|
@ -57,13 +57,4 @@ describe('Pages Yaml Editor wrapper', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('events', () => {
|
||||
const wrapper = mount(YamlEditor, defaultOptions);
|
||||
|
||||
it('emits touch if content is changed in editor', async () => {
|
||||
await wrapper.vm.editor.setValue('foo: boo');
|
||||
expect(wrapper.emitted('touch')).toEqual([expect.any(Array)]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Document, parseDocument } from 'yaml';
|
|||
import { GlProgressBar } from '@gitlab/ui';
|
||||
import { nextTick } from 'vue';
|
||||
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { mockTracking } from 'helpers/tracking_helper';
|
||||
import PipelineWizardWrapper, { i18n } from '~/pipeline_wizard/components/wrapper.vue';
|
||||
import WizardStep from '~/pipeline_wizard/components/step.vue';
|
||||
import CommitStep from '~/pipeline_wizard/components/commit.vue';
|
||||
|
@ -19,9 +20,11 @@ describe('Pipeline Wizard - wrapper.vue', () => {
|
|||
const steps = parseDocument(stepsYaml).toJS();
|
||||
|
||||
const getAsYamlNode = (value) => new Document(value).contents;
|
||||
const templateId = 'my-namespace/my-template';
|
||||
const createComponent = (props = {}, mountFn = shallowMountExtended) => {
|
||||
wrapper = mountFn(PipelineWizardWrapper, {
|
||||
propsData: {
|
||||
templateId,
|
||||
projectPath: '/user/repo',
|
||||
defaultBranch: 'main',
|
||||
filename: '.gitlab-ci.yml',
|
||||
|
@ -311,4 +314,126 @@ describe('Pipeline Wizard - wrapper.vue', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when commit step done', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('emits done', () => {
|
||||
expect(wrapper.emitted('done')).toBeUndefined();
|
||||
|
||||
wrapper.findComponent(CommitStep).vm.$emit('done');
|
||||
|
||||
expect(wrapper.emitted('done')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tracking', () => {
|
||||
let trackingSpy;
|
||||
const trackingCategory = `pipeline_wizard:${templateId}`;
|
||||
|
||||
const setUpTrackingSpy = () => {
|
||||
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
|
||||
};
|
||||
|
||||
it('tracks next button click event', () => {
|
||||
createComponent();
|
||||
setUpTrackingSpy();
|
||||
findFirstVisibleStep().vm.$emit('next');
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(trackingCategory, 'click_button', {
|
||||
category: trackingCategory,
|
||||
property: 'next',
|
||||
label: 'pipeline_wizard_navigation',
|
||||
extras: {
|
||||
fromStep: 0,
|
||||
toStep: 1,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('tracks back button click event', () => {
|
||||
createComponent();
|
||||
|
||||
// Navigate to step 1 without the spy set up
|
||||
findFirstVisibleStep().vm.$emit('next');
|
||||
|
||||
// Now enable the tracking spy
|
||||
setUpTrackingSpy();
|
||||
|
||||
findFirstVisibleStep().vm.$emit('back');
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(trackingCategory, 'click_button', {
|
||||
category: trackingCategory,
|
||||
property: 'back',
|
||||
label: 'pipeline_wizard_navigation',
|
||||
extras: {
|
||||
fromStep: 1,
|
||||
toStep: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('tracks back button click event on the commit step', () => {
|
||||
createComponent();
|
||||
|
||||
// Navigate to step 2 without the spy set up
|
||||
findFirstVisibleStep().vm.$emit('next');
|
||||
findFirstVisibleStep().vm.$emit('next');
|
||||
|
||||
// Now enable the tracking spy
|
||||
setUpTrackingSpy();
|
||||
|
||||
wrapper.findComponent(CommitStep).vm.$emit('back');
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(trackingCategory, 'click_button', {
|
||||
category: trackingCategory,
|
||||
property: 'back',
|
||||
label: 'pipeline_wizard_navigation',
|
||||
extras: {
|
||||
fromStep: 2,
|
||||
toStep: 1,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('tracks done event on the commit step', () => {
|
||||
createComponent();
|
||||
|
||||
// Navigate to step 2 without the spy set up
|
||||
findFirstVisibleStep().vm.$emit('next');
|
||||
findFirstVisibleStep().vm.$emit('next');
|
||||
|
||||
// Now enable the tracking spy
|
||||
setUpTrackingSpy();
|
||||
|
||||
wrapper.findComponent(CommitStep).vm.$emit('done');
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(trackingCategory, 'click_button', {
|
||||
category: trackingCategory,
|
||||
label: 'pipeline_wizard_commit',
|
||||
property: 'commit',
|
||||
});
|
||||
});
|
||||
|
||||
it('tracks when editor emits touch events', () => {
|
||||
createComponent();
|
||||
setUpTrackingSpy();
|
||||
|
||||
wrapper.findComponent(YamlEditor).vm.$emit('touch');
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(trackingCategory, 'edit', {
|
||||
category: trackingCategory,
|
||||
label: 'pipeline_wizard_editor_interaction',
|
||||
extras: {
|
||||
currentStep: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -71,6 +71,7 @@ bar: barVal
|
|||
`;
|
||||
|
||||
export const fullTemplate = `
|
||||
id: test/full-template
|
||||
title: some title
|
||||
description: some description
|
||||
filename: foo.yml
|
||||
|
@ -84,6 +85,7 @@ steps:
|
|||
`;
|
||||
|
||||
export const fullTemplateWithoutFilename = `
|
||||
id: test/full-template-no-filename
|
||||
title: some title
|
||||
description: some description
|
||||
steps:
|
||||
|
|
|
@ -59,6 +59,7 @@ describe('PipelineWizard', () => {
|
|||
defaultBranch,
|
||||
projectPath,
|
||||
filename: parseDocument(template).get('filename'),
|
||||
templateId: parseDocument(template).get('id'),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { editor as monacoEditor } from 'monaco-editor';
|
||||
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import { initIde } from '~/ide';
|
||||
import { initLegacyWebIDE } from '~/ide';
|
||||
import extendStore from '~/ide/stores/extend';
|
||||
import { getProject, getEmptyProject } from 'jest/../frontend_integration/test_helpers/fixtures';
|
||||
import { IDE_DATASET } from './mock_data';
|
||||
|
@ -16,7 +16,7 @@ export default (container, { isRepoEmpty = false, path = '', mrId = '' } = {}) =
|
|||
const el = document.createElement('div');
|
||||
Object.assign(el.dataset, IDE_DATASET, { project: JSON.stringify(project) });
|
||||
container.appendChild(el);
|
||||
const vm = initIde(el, { extendStore });
|
||||
const vm = initLegacyWebIDE(el, { extendStore });
|
||||
|
||||
// We need to dispose of editor Singleton things or tests will bump into eachother
|
||||
vm.$on('destroy', () => monacoEditor.getModels().forEach((model) => model.dispose()));
|
||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -1075,6 +1075,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.7.3.tgz#9ea641146436da388ffbad25d7f2abe0df52c235"
|
||||
integrity sha512-NMV++7Ew1FSBDN1xiZaauU9tfeSfgDHcOLpn+8bGpP+O5orUPm2Eu66R5eC5gkjBPaXosNAxNWtriee+aFk4+g==
|
||||
|
||||
"@gitlab/web-ide@0.0.1-dev-20220815034418":
|
||||
version "0.0.1-dev-20220815034418"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20220815034418.tgz#c4c4f72d6ffe1ba18fdfc452b30d10ff7cde0550"
|
||||
integrity sha512-cYTMAXfc+B549euU+IDHgFK0Rjwa5SNjs4coBJOD4eoZVvPd3YtHbArqqzmdO71SYjCqV1Bl4v0Jy4UnOJgcjQ==
|
||||
dependencies:
|
||||
mustache "^4.2.0"
|
||||
|
||||
"@graphql-eslint/eslint-plugin@3.10.7":
|
||||
version "3.10.7"
|
||||
resolved "https://registry.yarnpkg.com/@graphql-eslint/eslint-plugin/-/eslint-plugin-3.10.7.tgz#9a203e2084371eca933d88b73ce7a6bebbbb9872"
|
||||
|
@ -8905,6 +8912,11 @@ multicast-dns@^7.2.4:
|
|||
dns-packet "^5.2.2"
|
||||
thunky "^1.0.2"
|
||||
|
||||
mustache@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64"
|
||||
integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==
|
||||
|
||||
nanoid@^3.3.4:
|
||||
version "3.3.4"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
|
||||
|
|
Loading…
Reference in a new issue