Commit d7ea6d0c authored by Martin Wortschack's avatar Martin Wortschack Committed by Filipa Lacerda

Set project path on localStorage during onboarding

- bind events on new project page
parent 6ca4fddb
......@@ -46,11 +46,11 @@
= render_if_exists 'projects/new_ci_cd_only_project_tab', active_tab: active_tab
.tab-content.gitlab-tab-content
.tab-pane{ id: 'blank-project-pane', class: active_when(active_tab == 'blank'), role: 'tabpanel' }
.tab-pane.js-toggle-container{ id: 'blank-project-pane', class: active_when(active_tab == 'blank'), role: 'tabpanel' }
= form_for @project, html: { class: 'new_project' } do |f|
= render 'new_project_fields', f: f, project_name_id: "blank-project-name"
#create-from-template-pane.tab-pane.px-0.pb-0{ class: active_when(active_tab == 'template'), role: 'tabpanel' }
#create-from-template-pane.tab-pane.js-toggle-container.px-0.pb-0{ class: active_when(active_tab == 'template'), role: 'tabpanel' }
.card-slim.m-4.p-4
%div
- contributing_templates_url = 'https://gitlab.com/gitlab-org/project-templates/contributing'
......
import onboardingUtils from './utils';
import { AVAILABLE_TOURS } from './constants';
export const getProjectPath = () => {
let projectPath;
const activeTab = document.querySelector('.js-toggle-container.active');
const projectPathInput = activeTab.querySelector('#project_path');
const select = activeTab.querySelector('select.js-select-namespace');
if (select) {
const selectedOption = select.options[select.selectedIndex];
const { showPath } = selectedOption.dataset;
projectPath = `${showPath}/${projectPathInput.value}`;
} else {
projectPath = projectPathInput.value;
}
return projectPath;
};
/**
* Binds a submit event handler to the form on the "New project" page (for user onboarding only).
* It intercepts form submit and sets the project path of project to be created on the localStorage.
* The project path is used later in the onboarding process.
*
* @param {*} form The form we're going to add the submit event handler to
*/
export const bindOnboardingEvents = form => {
if (!form) {
return;
}
const onboardingState = onboardingUtils.getOnboardingLocalStorageState();
if (
!onboardingUtils.isOnboardingDismissed() &&
onboardingState &&
onboardingState.tourKey === AVAILABLE_TOURS.CREATE_PROJECT_TOUR
) {
form.addEventListener('submit', event => {
event.preventDefault();
event.stopPropagation();
const createdProjectPath = getProjectPath();
onboardingUtils.updateLocalStorage({ createdProjectPath });
form.submit();
});
}
};
......@@ -8,9 +8,14 @@ import {
const isOnboardingDismissed = () => Cookies.get(ONBOARDING_DISMISSED_COOKIE_NAME) === 'true';
const updateOnboardingDismissed = dismissed =>
const updateOnboardingDismissed = dismissed => {
Cookies.set(ONBOARDING_DISMISSED_COOKIE_NAME, dismissed);
if (dismissed && AccessorUtilities.isLocalStorageAccessSafe()) {
localStorage.removeItem(STORAGE_KEY);
}
};
const resetOnboardingLocalStorage = () => {
if (AccessorUtilities.isLocalStorageAccessSafe()) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(ONBOARDING_PROPS_DEFAULTS));
......
import '~/pages/projects/new/index';
import initCustomProjectTemplates from 'ee/projects/custom_project_templates';
import bindTrackEvents from 'ee/projects/track_project_new';
import { bindOnboardingEvents } from 'ee/onboarding/new_project';
document.addEventListener('DOMContentLoaded', () => {
initCustomProjectTemplates();
bindTrackEvents('.js-toggle-container');
bindOnboardingEvents(document.getElementById('new_project'));
});
import { bindOnboardingEvents, getProjectPath } from 'ee/onboarding/new_project';
import onboardingUtils from 'ee/onboarding/utils';
import { AVAILABLE_TOURS } from 'ee/onboarding/constants';
import { TEST_HOST } from 'helpers/test_constants';
import { setHTMLFixture } from 'helpers/fixtures';
describe('User onboarding new project utils', () => {
describe('getProjectPath', () => {
describe('when there exists a namespace select', () => {
beforeEach(() => {
setHTMLFixture(`
<div class='active tab-pane js-toggle-container'>
<input id="project_path" value="my-project"/>
<select class="js-select-namespace">
<option data-show-path="${TEST_HOST}/MyPath" selected="selected">MyPath</option>
<option data-show-path="${TEST_HOST}/foobar">foobar</option>
</select>
</div>
`);
});
it('returns the namespace and path', () => {
const result = getProjectPath();
expect(result).toEqual(`${TEST_HOST}/MyPath/my-project`);
});
});
describe("when there doesn't exist a namespace select", () => {
beforeEach(() => {
setHTMLFixture(`
<div class='active tab-pane js-toggle-container'>
<input id="project_path" value="my-project"/>
</div>
`);
});
it('returns the path only if there is no namespace select', () => {
const result = getProjectPath();
expect(result).toEqual('my-project');
});
});
});
describe('bindOnboardingEvents', () => {
let form;
let submitBtn;
let submitSpy;
beforeEach(() => {
setHTMLFixture(`
<div class='active tab-pane js-toggle-container'>
<form id="new_project">
<input id="project_path" value="my-project"/>
<input id="submitBtn" type="submit" value="Create project">
</form>
</div>
`);
submitSpy = jest
.fn()
.mockName('submit')
.mockImplementation(event => event.preventDefault());
form = document.getElementById('new_project');
submitBtn = document.getElementById('submitBtn');
form.addEventListener('submit', submitSpy);
jest.spyOn(form, 'submit').mockImplementation(() => {});
});
describe('when onboarding is not dismissed and there is an onboarding state on the local storage', () => {
beforeEach(() => {
jest.spyOn(onboardingUtils, 'isOnboardingDismissed').mockReturnValue(false);
jest.spyOn(onboardingUtils, 'getOnboardingLocalStorageState').mockReturnValue({
tourKey: AVAILABLE_TOURS.CREATE_PROJECT_TOUR,
});
});
it('adds the submit event listener to the form', () => {
jest.spyOn(form, 'addEventListener');
bindOnboardingEvents(form);
expect(form.addEventListener).toHaveBeenCalledWith('submit', jasmine.any(Function));
});
it('calls updateLocalStorage with the correct project path when the form is submitted', () => {
jest.spyOn(onboardingUtils, 'updateLocalStorage');
bindOnboardingEvents(form);
submitBtn.click();
expect(onboardingUtils.updateLocalStorage).toHaveBeenCalledWith({
createdProjectPath: 'my-project',
});
});
});
describe('when onboarding is dismissed', () => {
beforeEach(() => {
jest.spyOn(onboardingUtils, 'isOnboardingDismissed').mockReturnValue(true);
});
it('does not add the submit event listener to the form', () => {
jest.spyOn(form, 'addEventListener');
bindOnboardingEvents(form);
expect(form.addEventListener).not.toHaveBeenCalled();
});
it('does not call updateLocalStorage when the form is submitted', () => {
jest.spyOn(onboardingUtils, 'updateLocalStorage');
bindOnboardingEvents(form);
submitBtn.click();
expect(onboardingUtils.updateLocalStorage).not.toHaveBeenCalled();
});
});
describe('when the user is currently on a tour part different from the "Create Project Tour"', () => {
beforeEach(() => {
jest.spyOn(onboardingUtils, 'isOnboardingDismissed').mockReturnValue(false);
jest.spyOn(onboardingUtils, 'getOnboardingLocalStorageState').mockReturnValue({
tourKey: AVAILABLE_TOURS.GITLAB_GUIDED_TOUR,
});
});
it('does not add the submit event listener to the form', () => {
jest.spyOn(form, 'addEventListener');
bindOnboardingEvents(form);
expect(form.addEventListener).not.toHaveBeenCalled();
});
it('does not call updateLocalStorage when the form is submitted', () => {
jest.spyOn(onboardingUtils, 'updateLocalStorage');
bindOnboardingEvents(form);
submitBtn.click();
expect(onboardingUtils.updateLocalStorage).not.toHaveBeenCalled();
});
});
});
});
......@@ -28,10 +28,17 @@ describe('User onboarding utils', () => {
describe('updateOnboardingDismissed', () => {
it('set the dismissed state on the cookie', () => {
const dismissed = true;
Cookies.set(ONBOARDING_DISMISSED_COOKIE_NAME, dismissed);
onboardingUtils.updateOnboardingDismissed(true);
expect(Cookies.get(ONBOARDING_DISMISSED_COOKIE_NAME)).toBe(`${dismissed}`);
expect(Cookies.get(ONBOARDING_DISMISSED_COOKIE_NAME)).toBe('true');
});
it('removes onboarding related data from localStorage', () => {
spyOn(localStorage, 'removeItem');
onboardingUtils.updateOnboardingDismissed(true);
expect(localStorage.removeItem).toHaveBeenCalledWith(STORAGE_KEY);
});
});
......
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