From a75155b3585788656620a17dbfb58764f5e82422 Mon Sep 17 00:00:00 2001 From: Wouter Admiraal Date: Mon, 22 Feb 2021 15:46:12 +0100 Subject: [PATCH] SONAR-14496 Rely on sonar.core.serverBaseURL in CI tutorials --- .../tutorials/TutorialSelection.tsx | 33 ++++++++++++++--- .../tutorials/TutorialSelectionRenderer.tsx | 13 ++++++- .../__tests__/TutorialSelection-test.tsx | 37 +++++++++++++++++++ .../TutorialSelectionRenderer-test.tsx | 1 + .../TutorialSelection-test.tsx.snap | 1 + .../TutorialSelectionRenderer-test.tsx.snap | 2 + .../AzurePipelinesTutorial.tsx | 11 +++++- .../ServiceEndpointStepContent.tsx | 8 ++-- .../__tests__/AzurePipelinesTutorial-test.tsx | 1 + .../ServiceEndpointStepContent-test.tsx | 6 ++- .../ServiceEndpointStepContent-test.tsx.snap | 4 +- .../gitlabci/EnvironmentVariablesStep.tsx | 8 ++-- .../tutorials/gitlabci/GitLabCITutorial.tsx | 4 +- .../EnvironmentVariablesStep-test.tsx | 1 + .../__tests__/GitLabCITutorial-test.tsx | 1 + .../EnvironmentVariablesStep-test.tsx.snap | 4 +- .../GitLabCITutorial-test.tsx.snap | 1 + .../sonar-web/src/main/js/types/settings.ts | 3 +- 18 files changed, 116 insertions(+), 23 deletions(-) diff --git a/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx b/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx index 64a0eab297e..ddf797e83c2 100644 --- a/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx @@ -19,8 +19,11 @@ */ import * as React from 'react'; import { WithRouterProps } from 'react-router'; +import { getHostUrl } from 'sonar-ui-common/helpers/urls'; import { getAlmDefinitionsNoCatch, getProjectAlmBinding } from '../../api/alm-settings'; +import { getValues } from '../../api/settings'; import { AlmBindingDefinition, ProjectAlmBindingResponse } from '../../types/alm-settings'; +import { SettingsKey } from '../../types/settings'; import { withRouter } from '../hoc/withRouter'; import TutorialSelectionRenderer from './TutorialSelectionRenderer'; import { TutorialModes } from './types'; @@ -32,6 +35,7 @@ interface Props extends Pick { interface State { almBinding?: AlmBindingDefinition; + baseUrl: string; forceManual: boolean; loading: boolean; projectBinding?: ProjectAlmBindingResponse; @@ -40,13 +44,23 @@ interface State { export class TutorialSelection extends React.PureComponent { mounted = false; state: State = { + baseUrl: getHostUrl(), forceManual: true, loading: true }; - componentDidMount() { + async componentDidMount() { this.mounted = true; - this.fetchAlmBindings(); + + await Promise.all([this.fetchAlmBindings(), this.fetchBaseUrl()]); + + if (this.mounted) { + this.setState({ loading: false }); + } + } + + componentWillUnmount() { + this.mounted = false; } fetchAlmBindings = async () => { @@ -59,18 +73,26 @@ export class TutorialSelection extends React.PureComponent { if (this.mounted) { if (projectBinding === undefined) { - this.setState({ loading: false, forceManual: true }); + this.setState({ forceManual: true }); } else { let almBinding; if (almDefinitions !== undefined) { const specificDefinitions = almDefinitions[projectBinding.alm] as AlmBindingDefinition[]; almBinding = specificDefinitions.find(d => d.key === projectBinding.key); } - this.setState({ almBinding, forceManual: false, projectBinding, loading: false }); + this.setState({ almBinding, forceManual: false, projectBinding }); } } }; + fetchBaseUrl = async () => { + const settings = await getValues({ keys: SettingsKey.ServerBaseUrl }).catch(() => undefined); + const baseUrl = settings && settings.find(s => s.key === SettingsKey.ServerBaseUrl)?.value; + if (baseUrl && baseUrl.length > 0 && this.mounted) { + this.setState({ baseUrl }); + } + }; + handleSelectTutorial = (selectedTutorial: TutorialModes) => { const { router, @@ -85,7 +107,7 @@ export class TutorialSelection extends React.PureComponent { render() { const { component, currentUser, location } = this.props; - const { almBinding, forceManual, loading, projectBinding } = this.state; + const { almBinding, baseUrl, forceManual, loading, projectBinding } = this.state; const selectedTutorial: TutorialModes | undefined = forceManual ? TutorialModes.Manual @@ -94,6 +116,7 @@ export class TutorialSelection extends React.PureComponent { return ( ; @@ -138,6 +147,7 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender {selectedTutorial === TutorialModes.GitLabCI && projectBinding !== undefined && ( ({ + getHostUrl: jest.fn().mockReturnValue('http://host.url') +})); + jest.mock('../../../api/alm-settings', () => ({ getProjectAlmBinding: jest.fn().mockRejectedValue(null), getAlmDefinitionsNoCatch: jest.fn().mockRejectedValue(null) })); +jest.mock('../../../api/settings', () => ({ + getValues: jest.fn().mockResolvedValue([]) +})); + beforeEach(jest.clearAllMocks); it('should render correctly', () => { @@ -88,6 +99,32 @@ it('should handle selection', () => { ); }); +it('should fetch the correct baseUrl', async () => { + (getValues as jest.Mock) + .mockResolvedValueOnce([{ key: SettingsKey.ServerBaseUrl, value: '' }]) + .mockResolvedValueOnce([{ key: SettingsKey.ServerBaseUrl, value: 'http://sq.example.com' }]) + .mockRejectedValueOnce(null); + + let wrapper = shallowRender(); + + expect(getValues).toBeCalled(); + expect(getHostUrl).toBeCalled(); + + // No baseURL, fallback to the URL in the browser. + await waitAndUpdate(wrapper); + expect(wrapper.state().baseUrl).toBe('http://host.url'); + + // A baseURL was set. + wrapper = shallowRender(); + await waitAndUpdate(wrapper); + expect(wrapper.state().baseUrl).toBe('http://sq.example.com'); + + // Access denied, fallback to the URL in the browser. + wrapper = shallowRender(); + await waitAndUpdate(wrapper); + expect(wrapper.state().baseUrl).toBe('http://host.url'); +}); + function shallowRender(props: Partial = {}) { return shallow( = {}) { return shallow( }, { step: Steps.ServiceEndpoint, - content: + content: ( + + ) }, { step: Steps.BranchAnalysis, diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ServiceEndpointStepContent.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ServiceEndpointStepContent.tsx index a5d7daafdcb..04254a2482b 100644 --- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ServiceEndpointStepContent.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ServiceEndpointStepContent.tsx @@ -22,17 +22,17 @@ import { FormattedMessage } from 'react-intl'; import { Button } from 'sonar-ui-common/components/controls/buttons'; import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { getHostUrl } from 'sonar-ui-common/helpers/urls'; import EditTokenModal from '../components/EditTokenModal'; import SentenceWithHighlights from '../components/SentenceWithHighlights'; export interface ServiceEndpointStepProps { + baseUrl: string; component: T.Component; currentUser: T.LoggedInUser; } export default function ServiceEndpointStepContent(props: ServiceEndpointStepProps) { - const { component, currentUser } = props; + const { baseUrl, component, currentUser } = props; const [isModalVisible, toggleModal] = React.useState(false); @@ -61,8 +61,8 @@ export default function ServiceEndpointStepContent(props: ServiceEndpointStepPro )} id="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence" values={{ - url: {getHostUrl()}, - button: + url: {baseUrl}, + button: }} /> diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-test.tsx index bb41f38cbc1..1ff63b59569 100644 --- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-test.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-test.tsx @@ -101,6 +101,7 @@ it('should open a step when user click on it', () => { function shallowRender(props: Partial = {}) { return shallow( { function shallowRender() { return shallow( - + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ServiceEndpointStepContent-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ServiceEndpointStepContent-test.tsx.snap index 1596e44fa1c..7b52581aa12 100644 --- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ServiceEndpointStepContent-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ServiceEndpointStepContent-test.tsx.snap @@ -35,12 +35,12 @@ exports[`should render correctly 1`] = ` values={ Object { "button": , "url": - http://localhost + http://localhost:9000 , } } diff --git a/server/sonar-web/src/main/js/components/tutorials/gitlabci/EnvironmentVariablesStep.tsx b/server/sonar-web/src/main/js/components/tutorials/gitlabci/EnvironmentVariablesStep.tsx index 02934fa507a..47e02fc1dc1 100644 --- a/server/sonar-web/src/main/js/components/tutorials/gitlabci/EnvironmentVariablesStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/gitlabci/EnvironmentVariablesStep.tsx @@ -22,11 +22,11 @@ import { FormattedMessage } from 'react-intl'; import { Button } from 'sonar-ui-common/components/controls/buttons'; import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { getHostUrl } from 'sonar-ui-common/helpers/urls'; import EditTokenModal from '../components/EditTokenModal'; import Step from '../components/Step'; export interface EnvironmentVariablesStepProps { + baseUrl: string; component: T.Component; currentUser: T.LoggedInUser; finished: boolean; @@ -40,7 +40,7 @@ const pipelineDescriptionLinkLabel = translate( ); export default function EnvironmentVariablesStep(props: EnvironmentVariablesStepProps) { - const { component, currentUser, finished, open } = props; + const { baseUrl, component, currentUser, finished, open } = props; const [isModalVisible, toggleModal] = React.useState(false); @@ -139,9 +139,9 @@ export default function EnvironmentVariablesStep(props: EnvironmentVariablesStep defaultMessage={fieldValueTranslation} id="onboarding.tutorial.with.gitlab_ci.env_variables.step2" values={{ - extra: , + extra: , field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step2'), - value: {getHostUrl()} + value: {baseUrl} }} /> diff --git a/server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx index f7f08f40d94..0d53961bb59 100644 --- a/server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx @@ -36,13 +36,14 @@ export enum Steps { } export interface GitLabCITutorialProps { + baseUrl: string; component: T.Component; currentUser: T.LoggedInUser; projectBinding: ProjectAlmBindingResponse; } export default function GitLabCITutorial(props: GitLabCITutorialProps) { - const { component, currentUser, projectBinding } = props; + const { baseUrl, component, currentUser, projectBinding } = props; const [step, setStep] = React.useState(Steps.PROJECT_KEY); const [buildTool, setBuildTool] = React.useState(); @@ -71,6 +72,7 @@ export default function GitLabCITutorial(props: GitLabCITutorialProps) { /> Steps.ENV_VARIABLES} diff --git a/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/EnvironmentVariablesStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/EnvironmentVariablesStep-test.tsx index 57cf4a4be3c..d01b5e292e9 100644 --- a/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/EnvironmentVariablesStep-test.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/EnvironmentVariablesStep-test.tsx @@ -34,6 +34,7 @@ it('should render correctly', () => { function shallowRender(props: Partial = {}) { return shallow( { function shallowRender(props: Partial = {}) { return shallow( , "field": "onboarding.tutorial.with.gitlab_ci.env_variables.step2", "value": - http://localhost + http://localhost:9000 , } } diff --git a/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/GitLabCITutorial-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/GitLabCITutorial-test.tsx.snap index 00c4ef25f2c..e7e458b4f47 100644 --- a/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/GitLabCITutorial-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/GitLabCITutorial-test.tsx.snap @@ -41,6 +41,7 @@ exports[`should render correctly 1`] = ` setBuildTool={[Function]} />