@@ -21,6 +21,12 @@ import React from 'react'; | |||
(window as any).React = React; | |||
const MockObserver = { | |||
observe: jest.fn(), | |||
unobserve: jest.fn(), | |||
disconnect: jest.fn(), | |||
}; | |||
const content = document.createElement('div'); | |||
content.id = 'content'; | |||
document.documentElement.appendChild(content); | |||
@@ -37,3 +43,10 @@ jest.mock('../../src/main/js/helpers/l10n', () => ({ | |||
translateWithParameters: (messageKey: string, ...parameters: Array<string | number>) => | |||
[messageKey, ...parameters].join('.'), | |||
})); | |||
const MockIntersectionObserverEntries = [{ isIntersecting: true }]; | |||
(window as any).IntersectionObserver = jest.fn().mockImplementation((callback) => { | |||
callback(MockIntersectionObserverEntries, MockObserver); | |||
return MockObserver; | |||
}); |
@@ -11,6 +11,8 @@ | |||
"@emotion/react": "11.11.1", | |||
"@emotion/styled": "11.11.0", | |||
"@primer/octicons-react": "19.3.0", | |||
"@react-spring/rafz": "9.7.3", | |||
"@react-spring/web": "9.7.3", | |||
"@tanstack/react-query": "4.29.14", | |||
"classnames": "2.3.2", | |||
"clipboard": "2.0.11", |
@@ -216,7 +216,7 @@ function assertOtherStepIsCorrectlyRendered() { | |||
function assertFinishStepIsCorrectlyRendered() { | |||
expect( | |||
screen.getByRole('heading', { | |||
name: 'onboarding.tutorial.ci_outro.all_set.title', | |||
name: 'onboarding.tutorial.ci_outro.done', | |||
}) | |||
).toBeInTheDocument(); | |||
} |
@@ -28,7 +28,7 @@ import { | |||
} from '../../../../helpers/mocks/alm-settings'; | |||
import { mockComponent } from '../../../../helpers/mocks/component'; | |||
import { mockLanguage, mockLoggedInUser } from '../../../../helpers/testMocks'; | |||
import { renderApp, RenderContext } from '../../../../helpers/testReactTestingUtils'; | |||
import { RenderContext, renderApp } from '../../../../helpers/testReactTestingUtils'; | |||
import { AlmKeys } from '../../../../types/alm-settings'; | |||
import { Feature } from '../../../../types/features'; | |||
import { | |||
@@ -75,28 +75,28 @@ it('should follow and complete all steps', async () => { | |||
// Create/update configuration file step | |||
// Maven | |||
await user.click(ui.mavenBuildButton.get()); | |||
expect(getCopyToClipboardValue(1)).toMatchSnapshot('Maven: bitbucket-pipelines.yml'); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Maven: bitbucket-pipelines.yml'); | |||
// Gradle | |||
await user.click(ui.gradleBuildButton.get()); | |||
expect(getCopyToClipboardValue(2)).toMatchSnapshot('Groovy: build.gradle'); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Groovy: build.gradle'); | |||
await user.click(ui.gradleDSLButton(GradleBuildDSL.Kotlin).get()); | |||
expect(getCopyToClipboardValue(2)).toMatchSnapshot('Kotlin: build.gradle.kts'); | |||
expect(getCopyToClipboardValue(4)).toMatchSnapshot('Gradle: bitbucket-pipelines.yml'); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Kotlin: build.gradle.kts'); | |||
expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Gradle: bitbucket-pipelines.yml'); | |||
// .NET | |||
await user.click(ui.dotnetBuildButton.get()); | |||
expect(getCopyToClipboardValue(1)).toMatchSnapshot('.NET: bitbucket-pipelines.yml'); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('.NET: bitbucket-pipelines.yml'); | |||
// CFamily | |||
await user.click(ui.cFamilyBuildButton.get()); | |||
expect(getCopyToClipboardValue()).toMatchSnapshot('CFamily: sonar-project.properties'); | |||
expect(getCopyToClipboardValue(2)).toMatchSnapshot('CFamily: bitbucket-pipelines.yml'); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('CFamily: sonar-project.properties'); | |||
expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('CFamily: bitbucket-pipelines.yml'); | |||
// Other | |||
await user.click(ui.otherBuildButton.get()); | |||
expect(getCopyToClipboardValue()).toMatchSnapshot('Other: sonar-project.properties'); | |||
expect(getCopyToClipboardValue(2)).toMatchSnapshot('Other: .github/workflows/build.yml'); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Other: sonar-project.properties'); | |||
expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Other: .github/workflows/build.yml'); | |||
await user.click(ui.finishTutorialButton.get()); | |||
expect(ui.allSetSentence.get()).toBeInTheDocument(); |
@@ -17,15 +17,17 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import styled from '@emotion/styled'; | |||
import { animated, config, useSpring } from '@react-spring/web'; | |||
import { CheckIcon, FlagVisual, SubTitle } from 'design-system'; | |||
import * as React from 'react'; | |||
import withAvailableFeatures, { | |||
WithAvailableFeaturesProps, | |||
} from '../../../app/components/available-features/withAvailableFeatures'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { getBaseUrl } from '../../../helpers/system'; | |||
import useIntersectionObserver from '../../../hooks/useIntersectionObserver'; | |||
import { AlmKeys } from '../../../types/alm-settings'; | |||
import { Feature } from '../../../types/features'; | |||
import SentenceWithHighlights from './SentenceWithHighlights'; | |||
export interface AllSetProps extends WithAvailableFeaturesProps { | |||
alm: AlmKeys; | |||
@@ -33,65 +35,53 @@ export interface AllSetProps extends WithAvailableFeaturesProps { | |||
} | |||
export function AllSet(props: AllSetProps) { | |||
const outroRef = React.useRef<HTMLDivElement>(null); | |||
const { alm, willRefreshAutomatically } = props; | |||
const branchSupportEnabled = props.hasFeature(Feature.BranchSupport); | |||
const intersectionEntry = useIntersectionObserver(outroRef, { freezeOnceVisible: true }); | |||
const outroAnimation = useSpring({ | |||
from: { top: '200px' }, | |||
to: intersectionEntry?.isIntersecting ? { top: '0px' } : { top: '200px' }, | |||
config: config.wobbly, | |||
}); | |||
return ( | |||
<> | |||
<div className="abs-width-600"> | |||
<p className="big-spacer-bottom"> | |||
<SentenceWithHighlights | |||
highlightKeys={['all_set']} | |||
translationKey="onboarding.tutorial.ci_outro.all_set" | |||
/> | |||
<animated.div | |||
className="sw-flex sw-flex-col sw-items-center sw-relative" | |||
ref={outroRef} | |||
style={outroAnimation} | |||
> | |||
<FlagVisual /> | |||
<SubTitle className="sw-mt-3 sw-mb-12"> | |||
{translate('onboarding.tutorial.ci_outro.done')} | |||
</SubTitle> | |||
<MessageContainer> | |||
<p className="sw-body-sm sw-mb-4"> | |||
{translate('onboarding.tutorial.ci_outro.refresh_text')} | |||
</p> | |||
<div className="display-flex-row big-spacer-bottom"> | |||
<div> | |||
<img | |||
alt="" // Should be ignored by screen readers | |||
className="big-spacer-right" | |||
width={30} | |||
src={`${getBaseUrl()}/images/tutorials/commit.svg`} | |||
/> | |||
</div> | |||
<div> | |||
<p className="little-spacer-bottom"> | |||
<strong>{translate('onboarding.tutorial.ci_outro.commit')}</strong> | |||
</p> | |||
<p> | |||
{branchSupportEnabled | |||
? translate('onboarding.tutorial.ci_outro.commit.why', alm) | |||
: translate('onboarding.tutorial.ci_outro.commit.why.no_branches')} | |||
</p> | |||
</div> | |||
</div> | |||
{willRefreshAutomatically && ( | |||
<div className="display-flex-row"> | |||
<div> | |||
<img | |||
alt="" // Should be ignored by screen readers | |||
className="big-spacer-right" | |||
width={30} | |||
src={`${getBaseUrl()}/images/tutorials/refresh.svg`} | |||
/> | |||
</div> | |||
<div> | |||
<p className="little-spacer-bottom"> | |||
<strong>{translate('onboarding.tutorial.ci_outro.refresh')}</strong> | |||
</p> | |||
<p>{translate('onboarding.tutorial.ci_outro.refresh.why')}</p> | |||
</div> | |||
</div> | |||
)} | |||
</div> | |||
{willRefreshAutomatically && ( | |||
<div className="huge-spacer-bottom huge-spacer-top big-padded-top text-muted display-flex-center display-flex-justify-center"> | |||
<i className="spinner spacer-right" /> | |||
{translate('onboarding.tutorial.ci_outro.waiting_for_fist_analysis')} | |||
</div> | |||
)} | |||
</> | |||
<ul className="sw-mb-6"> | |||
<li className="sw-mb-4 sw-flex sw-items-center"> | |||
<CheckIcon className="sw-mr-2" /> | |||
{branchSupportEnabled | |||
? translate('onboarding.tutorial.ci_outro.commit.why', alm) | |||
: translate('onboarding.tutorial.ci_outro.commit.why.no_branches')} | |||
</li> | |||
{willRefreshAutomatically && ( | |||
<li className="sw-mb-4 sw-flex sw-items-center"> | |||
<CheckIcon className="sw-mr-2" /> | |||
{translate('onboarding.tutorial.ci_outro.refresh.why')} | |||
</li> | |||
)} | |||
</ul> | |||
</MessageContainer> | |||
</animated.div> | |||
); | |||
} | |||
const MessageContainer = styled.div` | |||
width: 840px; | |||
`; | |||
export default withAvailableFeatures(AllSet); |
@@ -17,11 +17,11 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { ClipboardIconButton, CodeSnippet, NumberedListItem } from 'design-system'; | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { ClipboardIconButton } from '../../../components/controls/clipboard'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import CodeSnippet from '../../common/CodeSnippet'; | |||
import { InlineSnippet } from './InlineSnippet'; | |||
export interface CreateYmlFileProps { | |||
yamlFileName: string; | |||
@@ -31,20 +31,20 @@ export interface CreateYmlFileProps { | |||
export default function CreateYmlFile(props: CreateYmlFileProps) { | |||
const { yamlTemplate, yamlFileName } = props; | |||
return ( | |||
<li className="abs-width-800"> | |||
<NumberedListItem> | |||
<FormattedMessage | |||
defaultMessage={translate('onboarding.tutorial.with.github_action.yaml.create_yml')} | |||
id="onboarding.tutorial.with.github_action.yaml.create_yml" | |||
values={{ | |||
file: ( | |||
<> | |||
<code className="rule">{yamlFileName}</code> | |||
<InlineSnippet snippet={yamlFileName} /> | |||
<ClipboardIconButton copyValue={yamlFileName} /> | |||
</> | |||
), | |||
}} | |||
/> | |||
<CodeSnippet snippet={yamlTemplate} /> | |||
</li> | |||
<CodeSnippet className="sw-p-6 sw-overflow-auto" snippet={yamlTemplate} language="yml" /> | |||
</NumberedListItem> | |||
); | |||
} |
@@ -17,9 +17,9 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { CodeSnippet, NumberedListItem } from 'design-system'; | |||
import * as React from 'react'; | |||
import { Component } from '../../../types/types'; | |||
import CodeSnippet from '../../common/CodeSnippet'; | |||
import SentenceWithFilename from './SentenceWithFilename'; | |||
export interface DefaultProjectKeyProps { | |||
@@ -31,12 +31,15 @@ const sonarProjectSnippet = (key: string) => `sonar.projectKey=${key}`; | |||
export default function DefaultProjectKey(props: DefaultProjectKeyProps) { | |||
const { component } = props; | |||
return ( | |||
<li className="abs-width-600"> | |||
<NumberedListItem> | |||
<SentenceWithFilename | |||
filename="sonar-project.properties" | |||
translationKey="onboarding.tutorial.other.project_key" | |||
/> | |||
<CodeSnippet snippet={sonarProjectSnippet(component.key)} /> | |||
</li> | |||
<CodeSnippet | |||
snippet={sonarProjectSnippet(component.key)} | |||
className="sw-p-8 sw-overflow-auto" | |||
/> | |||
</NumberedListItem> | |||
); | |||
} |
@@ -18,10 +18,10 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import classNames from 'classnames'; | |||
import { Card, LightLabel, StandoutLink } from 'design-system'; | |||
import React from 'react'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { getBaseUrl } from '../../../helpers/system'; | |||
import Link from '../../common/Link'; | |||
import { OSs, TutorialModes } from '../types'; | |||
import './GithubCFamilyExampleRepositories.css'; | |||
@@ -57,26 +57,21 @@ export default function GithubCFamilyExampleRepositories( | |||
const link = `https://github.com/orgs/sonarsource-cfamily-examples/repositories?q=${queryParams}`; | |||
return ( | |||
<div | |||
className={classNames( | |||
'github-cfamily-example-repositories-box big-padded boxed-group', | |||
className | |||
)} | |||
> | |||
<div className="display-flex-center"> | |||
<Card className={classNames('sw-p-4', className)}> | |||
<div> | |||
<img | |||
alt="" // Should be ignored by screen readers | |||
className="spacer-right" | |||
height={20} | |||
src={`${getBaseUrl()}/images/alm/github.svg`} | |||
/> | |||
<Link className="spacer-left big" target="_blank" to={link}> | |||
<StandoutLink target="_blank" to={link}> | |||
sonarsource-cfamily-examples | |||
</Link> | |||
</StandoutLink> | |||
</div> | |||
<p className="spacer-top"> | |||
<LightLabel as="p" className="sw-mt-4"> | |||
{translate('onboarding.tutorial.cfamily.examples_repositories_description')} | |||
</p> | |||
</div> | |||
</LightLabel> | |||
</Card> | |||
); | |||
} |
@@ -17,15 +17,15 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { ClipboardIconButton, CodeSnippet, NumberedListItem } from 'design-system/lib'; | |||
import React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { Component } from '../../../types/types'; | |||
import CodeSnippet from '../../common/CodeSnippet'; | |||
import { ClipboardIconButton } from '../../controls/clipboard'; | |||
import { GradleBuildDSL } from '../types'; | |||
import { buildGradleSnippet } from '../utils'; | |||
import GradleBuildSelection from './GradleBuildSelection'; | |||
import { InlineSnippet } from './InlineSnippet'; | |||
interface Props { | |||
component: Component; | |||
@@ -33,31 +33,35 @@ interface Props { | |||
export default function GradleBuild({ component }: Props) { | |||
return ( | |||
<li className="abs-width-600"> | |||
<NumberedListItem> | |||
<FormattedMessage | |||
defaultMessage={translate('onboarding.tutorial.with.yaml.gradle')} | |||
id="onboarding.tutorial.with.yaml.gradle" | |||
values={{ | |||
groovy: ( | |||
<> | |||
<code className="rule">{GradleBuildDSL.Groovy}</code> | |||
<InlineSnippet snippet={GradleBuildDSL.Groovy} /> | |||
<ClipboardIconButton copyValue={GradleBuildDSL.Groovy} /> | |||
</> | |||
), | |||
kotlin: ( | |||
<> | |||
<code className="rule">{GradleBuildDSL.Kotlin}</code> | |||
<InlineSnippet snippet={GradleBuildDSL.Kotlin} /> | |||
<ClipboardIconButton copyValue={GradleBuildDSL.Kotlin} /> | |||
</> | |||
), | |||
sq: <code className="rule">org.sonarqube</code>, | |||
sq: <InlineSnippet snippet="org.sonarqube" />, | |||
}} | |||
/> | |||
<GradleBuildSelection className="big-spacer-top big-spacer-bottom"> | |||
<GradleBuildSelection className="sw-my-4"> | |||
{(build) => ( | |||
<CodeSnippet snippet={buildGradleSnippet(component.key, component.name, build)} /> | |||
<CodeSnippet | |||
language="gradle" | |||
className="sw-p-6" | |||
snippet={buildGradleSnippet(component.key, component.name, build)} | |||
/> | |||
)} | |||
</GradleBuildSelection> | |||
</li> | |||
</NumberedListItem> | |||
); | |||
} |
@@ -17,12 +17,21 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import classNames from 'classnames'; | |||
import { CodeSnippet } from 'design-system'; | |||
import * as React from 'react'; | |||
import { FCProps } from '../../../types/misc'; | |||
export function InlineSnippet({ snippet }: Pick<FCProps<typeof CodeSnippet>, 'snippet'>) { | |||
export function InlineSnippet({ | |||
snippet, | |||
className, | |||
}: Pick<FCProps<typeof CodeSnippet>, 'snippet'> & { className?: string }) { | |||
return ( | |||
<CodeSnippet className="sw-code sw-inline-block sw-px-1" noCopy isOneLine snippet={snippet} /> | |||
<CodeSnippet | |||
className={classNames('sw-code sw-inline-block sw-px-1', className)} | |||
noCopy | |||
isOneLine | |||
snippet={snippet} | |||
/> | |||
); | |||
} |
@@ -28,23 +28,32 @@ export interface RenderOptionsProps { | |||
optionLabelKey: string; | |||
options: string[]; | |||
titleLabelKey?: string; | |||
setDone?: (doneStatus: boolean) => void; | |||
} | |||
export default function RenderOptions({ | |||
checked, | |||
label, | |||
onCheck, | |||
setDone, | |||
optionLabelKey, | |||
options, | |||
titleLabelKey, | |||
}: RenderOptionsProps) { | |||
const onChange = (checked: string) => { | |||
if (setDone) { | |||
setDone(true); | |||
} | |||
onCheck(checked); | |||
}; | |||
return ( | |||
<div className="sw-mt-4"> | |||
{titleLabelKey && <label className="sw-block sw-mb-1">{translate(titleLabelKey)}</label>} | |||
<ToggleButton | |||
label={label} | |||
onChange={onCheck} | |||
onChange={onChange} | |||
options={options.map((build) => ({ | |||
label: translate(optionLabelKey, build), | |||
value: build, |
@@ -20,6 +20,7 @@ | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { InlineSnippet } from './InlineSnippet'; | |||
export interface SentenceWithFilenameProps { | |||
filename: string; | |||
@@ -36,7 +37,7 @@ export default function SentenceWithFilename({ | |||
defaultMessage={translate(translationKey, 'sentence')} | |||
id={`${translationKey}.sentence`} | |||
values={{ | |||
file: <code>{filename}</code>, | |||
file: <InlineSnippet snippet={filename} />, | |||
}} | |||
/> | |||
</span> |
@@ -17,9 +17,9 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { ButtonSecondary, NumberedListItem } from 'design-system'; | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { Button } from '../../../components/controls/buttons'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { Component } from '../../../types/types'; | |||
import { LoggedInUser } from '../../../types/users'; | |||
@@ -39,21 +39,25 @@ export default function TokenStepGenerator(props: TokenStepGeneratorProps) { | |||
return ( | |||
<> | |||
<li className="big-spacer-bottom"> | |||
<NumberedListItem> | |||
<FormattedMessage | |||
defaultMessage={translate('onboarding.tutorial.env_variables')} | |||
id="onboarding.tutorial.env_variables" | |||
values={{ | |||
extra: ( | |||
<Button className="spacer-left" onClick={toggleTokenModal}> | |||
<ButtonSecondary className="sw-ml-2" onClick={toggleTokenModal}> | |||
{translate('onboarding.token.generate.long')} | |||
</Button> | |||
</ButtonSecondary> | |||
), | |||
field: ( | |||
<span className="sw-body-sm-highlight"> | |||
{translate('onboarding.tutorial.env_variables.field')} | |||
</span> | |||
), | |||
field: <strong>{translate('onboarding.tutorial.env_variables.field')}</strong>, | |||
value: translate('onboarding.tutorial.env_variables.token_generator.value'), | |||
}} | |||
/> | |||
</li> | |||
</NumberedListItem> | |||
{isModalVisible && ( | |||
<EditTokenModal component={component} currentUser={currentUser} onClose={closeTokenModal} /> | |||
)} |
@@ -17,6 +17,7 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { NumberedList, NumberedListItem } from 'design-system'; | |||
import * as React from 'react'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { withCLanguageFeature } from '../../hoc/withCLanguageFeature'; | |||
@@ -26,6 +27,7 @@ import { BuildTools } from '../types'; | |||
export interface YamlFileStepProps { | |||
children?: (buildTool: BuildTools) => React.ReactElement<{}>; | |||
hasCLanguageFeature: boolean; | |||
setDone?: (doneStatus: boolean) => void; | |||
} | |||
export function YamlFileStep(props: YamlFileStepProps) { | |||
@@ -40,8 +42,8 @@ export function YamlFileStep(props: YamlFileStepProps) { | |||
const [buildToolSelected, setBuildToolSelected] = React.useState<BuildTools>(); | |||
return ( | |||
<ol className="list-styled big-spacer-top big-spacer-bottom"> | |||
<li className="abs-width-600"> | |||
<NumberedList> | |||
<NumberedListItem> | |||
{translate('onboarding.build')} | |||
<RenderOptions | |||
label={translate('onboarding.build')} | |||
@@ -49,10 +51,11 @@ export function YamlFileStep(props: YamlFileStepProps) { | |||
onCheck={(value) => setBuildToolSelected(value as BuildTools)} | |||
options={buildTools} | |||
optionLabelKey="onboarding.build" | |||
setDone={props.setDone} | |||
/> | |||
</li> | |||
</NumberedListItem> | |||
{children && buildToolSelected && children(buildToolSelected)} | |||
</ol> | |||
</NumberedList> | |||
); | |||
} | |||
@@ -34,7 +34,6 @@ export interface AnalysisCommandProps extends WithAvailableFeaturesProps { | |||
buildTool: BuildTools; | |||
mainBranchName: string; | |||
component: Component; | |||
onDone: () => void; | |||
} | |||
export function AnalysisCommand(props: AnalysisCommandProps) { | |||
@@ -48,7 +47,6 @@ export function AnalysisCommand(props: AnalysisCommandProps) { | |||
branchesEnabled={branchSupportEnabled} | |||
mainBranchName={mainBranchName} | |||
component={component} | |||
onDone={props.onDone} | |||
/> | |||
); | |||
case BuildTools.Gradle: | |||
@@ -57,7 +55,6 @@ export function AnalysisCommand(props: AnalysisCommandProps) { | |||
branchesEnabled={branchSupportEnabled} | |||
mainBranchName={mainBranchName} | |||
component={component} | |||
onDone={props.onDone} | |||
/> | |||
); | |||
case BuildTools.DotNet: | |||
@@ -66,7 +63,6 @@ export function AnalysisCommand(props: AnalysisCommandProps) { | |||
branchesEnabled={branchSupportEnabled} | |||
mainBranchName={mainBranchName} | |||
component={component} | |||
onDone={props.onDone} | |||
/> | |||
); | |||
case BuildTools.CFamily: | |||
@@ -75,7 +71,6 @@ export function AnalysisCommand(props: AnalysisCommandProps) { | |||
branchesEnabled={branchSupportEnabled} | |||
mainBranchName={mainBranchName} | |||
component={component} | |||
onDone={props.onDone} | |||
/> | |||
); | |||
case BuildTools.Other: | |||
@@ -84,7 +79,6 @@ export function AnalysisCommand(props: AnalysisCommandProps) { | |||
branchesEnabled={branchSupportEnabled} | |||
mainBranchName={mainBranchName} | |||
component={component} | |||
onDone={props.onDone} | |||
/> | |||
); | |||
} |
@@ -17,6 +17,7 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { BasicSeparator, TutorialStep, TutorialStepList } from 'design-system'; | |||
import * as React from 'react'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { | |||
@@ -26,18 +27,11 @@ import { | |||
} from '../../../types/alm-settings'; | |||
import { Component } from '../../../types/types'; | |||
import { LoggedInUser } from '../../../types/users'; | |||
import AllSetStep from '../components/AllSetStep'; | |||
import Step from '../components/Step'; | |||
import AllSet from '../components/AllSet'; | |||
import YamlFileStep from '../components/YamlFileStep'; | |||
import AnalysisCommand from './AnalysisCommand'; | |||
import SecretStep from './SecretStep'; | |||
export enum Steps { | |||
CREATE_SECRET = 1, | |||
YAML = 2, | |||
ALL_SET = 3, | |||
} | |||
export interface GitHubActionTutorialProps { | |||
almBinding?: AlmSettingsInstance; | |||
baseUrl: string; | |||
@@ -49,6 +43,7 @@ export interface GitHubActionTutorialProps { | |||
} | |||
export default function GitHubActionTutorial(props: GitHubActionTutorialProps) { | |||
const [done, setDone] = React.useState<boolean>(false); | |||
const { | |||
almBinding, | |||
baseUrl, | |||
@@ -58,52 +53,37 @@ export default function GitHubActionTutorial(props: GitHubActionTutorialProps) { | |||
mainBranchName, | |||
willRefreshAutomatically, | |||
} = props; | |||
const [step, setStep] = React.useState<Steps>(Steps.CREATE_SECRET); | |||
return ( | |||
<> | |||
<Step | |||
finished={step > Steps.CREATE_SECRET} | |||
onOpen={() => setStep(Steps.CREATE_SECRET)} | |||
open={step === Steps.CREATE_SECRET} | |||
renderForm={() => ( | |||
<SecretStep | |||
almBinding={almBinding} | |||
baseUrl={baseUrl} | |||
component={component} | |||
currentUser={currentUser} | |||
projectBinding={projectBinding} | |||
onDone={() => setStep(Steps.YAML)} | |||
<TutorialStepList className="sw-mb-8"> | |||
<TutorialStep title={translate('onboarding.tutorial.with.github_action.create_secret.title')}> | |||
<SecretStep | |||
almBinding={almBinding} | |||
baseUrl={baseUrl} | |||
component={component} | |||
currentUser={currentUser} | |||
projectBinding={projectBinding} | |||
/> | |||
</TutorialStep> | |||
<TutorialStep title={translate('onboarding.tutorial.with.github_action.yaml.title')}> | |||
<YamlFileStep setDone={setDone}> | |||
{(buildTool) => ( | |||
<AnalysisCommand | |||
buildTool={buildTool} | |||
mainBranchName={mainBranchName} | |||
component={component} | |||
/> | |||
)} | |||
</YamlFileStep> | |||
</TutorialStep> | |||
{done && ( | |||
<> | |||
<BasicSeparator className="sw-my-10" /> | |||
<AllSet | |||
alm={almBinding?.alm || AlmKeys.GitHub} | |||
willRefreshAutomatically={willRefreshAutomatically} | |||
/> | |||
)} | |||
stepNumber={Steps.CREATE_SECRET} | |||
stepTitle={translate('onboarding.tutorial.with.github_action.create_secret.title')} | |||
/> | |||
<Step | |||
finished={step > Steps.YAML} | |||
onOpen={() => setStep(Steps.YAML)} | |||
open={step === Steps.YAML} | |||
renderForm={() => ( | |||
<YamlFileStep> | |||
{(buildTool) => ( | |||
<AnalysisCommand | |||
buildTool={buildTool} | |||
mainBranchName={mainBranchName} | |||
component={component} | |||
onDone={() => setStep(Steps.ALL_SET)} | |||
/> | |||
)} | |||
</YamlFileStep> | |||
)} | |||
stepNumber={Steps.YAML} | |||
stepTitle={translate('onboarding.tutorial.with.github_action.yaml.title')} | |||
/> | |||
<AllSetStep | |||
alm={almBinding?.alm || AlmKeys.GitHub} | |||
open={step === Steps.ALL_SET} | |||
stepNumber={Steps.ALL_SET} | |||
willRefreshAutomatically={willRefreshAutomatically} | |||
/> | |||
</> | |||
</> | |||
)} | |||
</TutorialStepList> | |||
); | |||
} |
@@ -17,14 +17,20 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { | |||
BasicSeparator, | |||
ClipboardIconButton, | |||
NumberedList, | |||
NumberedListItem, | |||
StandoutLink, | |||
} from 'design-system'; | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { Button } from '../../../components/controls/buttons'; | |||
import { ClipboardIconButton } from '../../../components/controls/clipboard'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../../types/alm-settings'; | |||
import { Component } from '../../../types/types'; | |||
import { LoggedInUser } from '../../../types/users'; | |||
import { InlineSnippet } from '../components/InlineSnippet'; | |||
import SentenceWithHighlights from '../components/SentenceWithHighlights'; | |||
import TokenStepGenerator from '../components/TokenStepGenerator'; | |||
import { buildGithubLink } from '../utils'; | |||
@@ -35,97 +41,94 @@ export interface SecretStepProps { | |||
component: Component; | |||
currentUser: LoggedInUser; | |||
projectBinding?: ProjectAlmBindingResponse; | |||
onDone: () => void; | |||
} | |||
export default function SecretStep(props: SecretStepProps) { | |||
const { almBinding, baseUrl, component, currentUser, projectBinding } = props; | |||
return ( | |||
<div className="boxed-group-inner"> | |||
<p className="big-spacer-bottom"> | |||
<FormattedMessage | |||
defaultMessage={translate('onboarding.tutorial.with.github_action.secret.intro')} | |||
id="onboarding.tutorial.with.github_action.secret.intro" | |||
values={{ | |||
settings_secret: | |||
almBinding && projectBinding ? ( | |||
<a | |||
href={`${buildGithubLink(almBinding, projectBinding)}/settings/secrets`} | |||
target="_blank" | |||
rel="noopener noreferrer" | |||
> | |||
{translate('onboarding.tutorial.with.github_action.secret.intro.link')} | |||
</a> | |||
) : ( | |||
<strong> | |||
{translate('onboarding.tutorial.with.github_action.secret.intro.link')} | |||
</strong> | |||
), | |||
}} | |||
/> | |||
</p> | |||
<ol className="list-styled"> | |||
<li> | |||
<> | |||
<FormattedMessage | |||
defaultMessage={translate('onboarding.tutorial.with.github_action.secret.intro')} | |||
id="onboarding.tutorial.with.github_action.secret.intro" | |||
values={{ | |||
settings_secret: | |||
almBinding && projectBinding ? ( | |||
<StandoutLink | |||
to={`${buildGithubLink(almBinding, projectBinding)}/settings/secrets`} | |||
target="_blank" | |||
rel="noopener noreferrer" | |||
> | |||
{translate('onboarding.tutorial.with.github_action.secret.intro.link')} | |||
</StandoutLink> | |||
) : ( | |||
<span className="sw-body-sm-highlight"> | |||
{translate('onboarding.tutorial.with.github_action.secret.intro.link')} | |||
</span> | |||
), | |||
}} | |||
/> | |||
<NumberedList> | |||
<NumberedListItem> | |||
<SentenceWithHighlights | |||
translationKey="onboarding.tutorial.with.github_action.secret.new" | |||
highlightKeys={['new_secret']} | |||
/> | |||
</li> | |||
<li> | |||
</NumberedListItem> | |||
<NumberedListItem> | |||
<SentenceWithHighlights | |||
translationKey="onboarding.tutorial.with.github_action.secret.name" | |||
highlightKeys={['name']} | |||
/> | |||
<code className="rule little-spacer-left">SONAR_TOKEN</code> | |||
<InlineSnippet snippet="SONAR_TOKEN" className="sw-ml-1" /> | |||
<ClipboardIconButton copyValue="SONAR_TOKEN" /> | |||
</li> | |||
</NumberedListItem> | |||
<TokenStepGenerator component={component} currentUser={currentUser} /> | |||
<li> | |||
<NumberedListItem> | |||
<SentenceWithHighlights | |||
translationKey="onboarding.tutorial.with.github_action.secret.add" | |||
highlightKeys={['add_secret']} | |||
/> | |||
</li> | |||
</ol> | |||
<hr className="no-horizontal-margins" /> | |||
<ol className="list-styled big-spacer-top big-spacer-bottom"> | |||
<li> | |||
</NumberedListItem> | |||
</NumberedList> | |||
<BasicSeparator className="sw-my-6" /> | |||
<NumberedList> | |||
<NumberedListItem> | |||
<SentenceWithHighlights | |||
translationKey="onboarding.tutorial.with.github_action.secret.new" | |||
highlightKeys={['new_secret']} | |||
/> | |||
</li> | |||
<li> | |||
</NumberedListItem> | |||
<NumberedListItem> | |||
<SentenceWithHighlights | |||
translationKey="onboarding.tutorial.with.github_action.secret.name" | |||
highlightKeys={['name']} | |||
/> | |||
<code className="rule little-spacer-left">SONAR_HOST_URL</code> | |||
<InlineSnippet snippet="SONAR_HOST_URL" className="sw-ml-1" /> | |||
<ClipboardIconButton copyValue="SONAR_HOST_URL" /> | |||
</li> | |||
<li className="big-spacer-bottom"> | |||
</NumberedListItem> | |||
<NumberedListItem> | |||
<FormattedMessage | |||
defaultMessage={translate('onboarding.tutorial.env_variables')} | |||
id="onboarding.tutorial.env_variables" | |||
values={{ | |||
extra: <ClipboardIconButton copyValue={baseUrl} />, | |||
field: <strong>{translate('onboarding.tutorial.env_variables.field')}</strong>, | |||
value: <code className="rule">{baseUrl}</code>, | |||
field: ( | |||
<span className="sw-body-sm-highlight"> | |||
{translate('onboarding.tutorial.env_variables.field')} | |||
</span> | |||
), | |||
value: <InlineSnippet snippet={baseUrl} className="sw-ml-1" />, | |||
}} | |||
/> | |||
</li> | |||
<li> | |||
</NumberedListItem> | |||
<NumberedListItem> | |||
<SentenceWithHighlights | |||
translationKey="onboarding.tutorial.with.github_action.secret.add" | |||
highlightKeys={['add_secret']} | |||
/> | |||
</li> | |||
</ol> | |||
<Button onClick={props.onDone}>{translate('continue')}</Button> | |||
</div> | |||
</NumberedListItem> | |||
</NumberedList> | |||
</> | |||
); | |||
} |
@@ -28,7 +28,7 @@ import { | |||
} from '../../../../helpers/mocks/alm-settings'; | |||
import { mockComponent } from '../../../../helpers/mocks/component'; | |||
import { mockLanguage, mockLoggedInUser } from '../../../../helpers/testMocks'; | |||
import { renderApp, RenderContext } from '../../../../helpers/testReactTestingUtils'; | |||
import { RenderContext, renderApp } from '../../../../helpers/testReactTestingUtils'; | |||
import { AlmKeys } from '../../../../types/alm-settings'; | |||
import { Feature } from '../../../../types/features'; | |||
import { | |||
@@ -65,45 +65,49 @@ it('should follow and complete all steps', async () => { | |||
expect(await ui.secretsStepTitle.find()).toBeInTheDocument(); | |||
// Env variables step | |||
expect(getCopyToClipboardValue()).toMatchSnapshot('sonar token key'); | |||
expect(getCopyToClipboardValue(1)).toMatchSnapshot('sonarqube host url key'); | |||
expect(getCopyToClipboardValue(2)).toMatchSnapshot('sonarqube host url value'); | |||
await user.click(ui.continueButton.get()); | |||
expect(getCopyToClipboardValue(0, 'Copy to clipboard')).toMatchSnapshot('sonar token key'); | |||
expect(getCopyToClipboardValue(1, 'Copy to clipboard')).toMatchSnapshot('sonarqube host url key'); | |||
expect(getCopyToClipboardValue(2, 'Copy to clipboard')).toMatchSnapshot( | |||
'sonarqube host url value' | |||
); | |||
// Create/update configuration file step | |||
// Maven | |||
await user.click(ui.mavenBuildButton.get()); | |||
expect(getCopyToClipboardValue(1)).toMatchSnapshot('Maven: .github/workflows/build.yml'); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Maven: .github/workflows/build.yml'); | |||
// Gradle | |||
await user.click(ui.gradleBuildButton.get()); | |||
expect(getCopyToClipboardValue(2)).toMatchSnapshot('Groovy: build.gradle'); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Groovy: build.gradle'); | |||
await user.click(ui.gradleDSLButton(GradleBuildDSL.Kotlin).get()); | |||
expect(getCopyToClipboardValue(2)).toMatchSnapshot('Kotlin: build.gradle.kts'); | |||
expect(getCopyToClipboardValue(4)).toMatchSnapshot('Gradle: .github/workflows/build.yml'); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Kotlin: build.gradle.kts'); | |||
expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Gradle: .github/workflows/build.yml'); | |||
// .NET | |||
await user.click(ui.dotnetBuildButton.get()); | |||
expect(getCopyToClipboardValue(1)).toMatchSnapshot('.NET: .github/workflows/build.yml'); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('.NET: .github/workflows/build.yml'); | |||
// CFamily | |||
await user.click(ui.cFamilyBuildButton.get()); | |||
expect(getCopyToClipboardValue()).toMatchSnapshot('CFamily: sonar-project.properties'); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('CFamily: sonar-project.properties'); | |||
await user.click(ui.linuxButton.get()); | |||
expect(getCopyToClipboardValue(2)).toMatchSnapshot('CFamily Linux: .github/workflows/build.yml'); | |||
expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( | |||
'CFamily Linux: .github/workflows/build.yml' | |||
); | |||
await user.click(ui.windowsButton.get()); | |||
expect(getCopyToClipboardValue(2)).toMatchSnapshot( | |||
expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( | |||
'CFamily Windows: .github/workflows/build.yml' | |||
); | |||
await user.click(ui.macosButton.get()); | |||
expect(getCopyToClipboardValue(2)).toMatchSnapshot('CFamily MacOS: .github/workflows/build.yml'); | |||
expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( | |||
'CFamily MacOS: .github/workflows/build.yml' | |||
); | |||
// Other | |||
await user.click(ui.otherBuildButton.get()); | |||
expect(getCopyToClipboardValue()).toMatchSnapshot('Other: sonar-project.properties'); | |||
expect(getCopyToClipboardValue(2)).toMatchSnapshot('Other: .github/workflows/build.yml'); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Other: sonar-project.properties'); | |||
expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Other: .github/workflows/build.yml'); | |||
await user.click(ui.finishTutorialButton.get()); | |||
expect(ui.allSetSentence.get()).toBeInTheDocument(); | |||
}); | |||
@@ -116,7 +120,7 @@ it('should generate/delete a new token or use existing one', async () => { | |||
// Generate token | |||
await user.click(ui.genTokenDialogButton.get()); | |||
await user.click(ui.generateTokenButton.get()); | |||
expect(getCopyToClipboardValue(3)).toEqual('generatedtoken2'); | |||
expect(getCopyToClipboardValue()).toEqual('generatedtoken2'); | |||
// Revoke current token and create new one | |||
await user.click(ui.deleteTokenButton.get()); | |||
@@ -124,7 +128,7 @@ it('should generate/delete a new token or use existing one', async () => { | |||
await selectEvent.select(ui.expiresInSelect.get(), 'users.tokens.expiration.365'); | |||
await user.click(ui.generateTokenButton.get()); | |||
expect(ui.tokenValue.get()).toBeInTheDocument(); | |||
await user.click(ui.continueButton.getAll()[1]); | |||
await user.click(ui.continueButton.getAll()[0]); | |||
expect(ui.tokenValue.query()).not.toBeInTheDocument(); | |||
}); | |||
@@ -141,9 +145,7 @@ it('navigates between steps', async () => { | |||
// If project is bound, link to repo is visible | |||
expect(await ui.linkToRepo.find()).toBeInTheDocument(); | |||
await user.click(await ui.continueButton.find()); | |||
await user.click(ui.mavenBuildButton.get()); | |||
await user.click(ui.finishTutorialButton.get()); | |||
expect(ui.allSetSentence.get()).toBeInTheDocument(); | |||
await user.click(ui.ymlFileStepTitle.get()); |
@@ -17,13 +17,13 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { NumberedListItem } from 'design-system'; | |||
import * as React from 'react'; | |||
import { translate } from '../../../../helpers/l10n'; | |||
import { Component } from '../../../../types/types'; | |||
import { CompilationInfo } from '../../components/CompilationInfo'; | |||
import CreateYmlFile from '../../components/CreateYmlFile'; | |||
import DefaultProjectKey from '../../components/DefaultProjectKey'; | |||
import FinishButton from '../../components/FinishButton'; | |||
import GithubCFamilyExampleRepositories from '../../components/GithubCFamilyExampleRepositories'; | |||
import RenderOptions from '../../components/RenderOptions'; | |||
import { OSs, TutorialModes } from '../../types'; | |||
@@ -33,7 +33,6 @@ export interface CFamilyProps { | |||
branchesEnabled?: boolean; | |||
mainBranchName: string; | |||
component: Component; | |||
onDone: () => void; | |||
} | |||
const STEPS = { | |||
@@ -118,12 +117,12 @@ const STEPS = { | |||
export default function CFamily(props: CFamilyProps) { | |||
const { component, branchesEnabled, mainBranchName } = props; | |||
const [os, setOs] = React.useState<undefined | OSs>(); | |||
const [os, setOs] = React.useState<undefined | OSs>(OSs.Linux); | |||
return ( | |||
<> | |||
<DefaultProjectKey component={component} /> | |||
<li className="abs-width-600"> | |||
<NumberedListItem> | |||
<span>{translate('onboarding.build.other.os')}</span> | |||
<RenderOptions | |||
label={translate('onboarding.build.other.os')} | |||
@@ -134,12 +133,12 @@ export default function CFamily(props: CFamilyProps) { | |||
/> | |||
{os && ( | |||
<GithubCFamilyExampleRepositories | |||
className="big-spacer-top" | |||
className="sw-mt-4" | |||
os={os} | |||
ci={TutorialModes.GitHubActions} | |||
/> | |||
)} | |||
</li> | |||
</NumberedListItem> | |||
{os && ( | |||
<> | |||
<CreateYmlFile | |||
@@ -151,8 +150,7 @@ export default function CFamily(props: CFamilyProps) { | |||
STEPS[os] | |||
)} | |||
/> | |||
<CompilationInfo className="abs-width-800" /> | |||
<FinishButton onClick={props.onDone} /> | |||
<CompilationInfo /> | |||
</> | |||
)} | |||
</> |
@@ -20,7 +20,6 @@ | |||
import * as React from 'react'; | |||
import { Component } from '../../../../types/types'; | |||
import CreateYmlFile from '../../components/CreateYmlFile'; | |||
import FinishButton from '../../components/FinishButton'; | |||
import { GITHUB_ACTIONS_RUNS_ON_WINDOWS } from '../constants'; | |||
import { generateGitHubActionsYaml } from '../utils'; | |||
@@ -28,7 +27,6 @@ export interface DotNetProps { | |||
branchesEnabled?: boolean; | |||
mainBranchName: string; | |||
component: Component; | |||
onDone: () => void; | |||
} | |||
function dotnetYamlSteps(projectKey: string) { | |||
@@ -69,17 +67,14 @@ function dotnetYamlSteps(projectKey: string) { | |||
export default function DotNet(props: DotNetProps) { | |||
const { component, branchesEnabled, mainBranchName } = props; | |||
return ( | |||
<> | |||
<CreateYmlFile | |||
yamlFileName=".github/workflows/build.yml" | |||
yamlTemplate={generateGitHubActionsYaml( | |||
mainBranchName, | |||
!!branchesEnabled, | |||
GITHUB_ACTIONS_RUNS_ON_WINDOWS, | |||
dotnetYamlSteps(component.key) | |||
)} | |||
/> | |||
<FinishButton onClick={props.onDone} /> | |||
</> | |||
<CreateYmlFile | |||
yamlFileName=".github/workflows/build.yml" | |||
yamlTemplate={generateGitHubActionsYaml( | |||
mainBranchName, | |||
!!branchesEnabled, | |||
GITHUB_ACTIONS_RUNS_ON_WINDOWS, | |||
dotnetYamlSteps(component.key) | |||
)} | |||
/> | |||
); | |||
} |
@@ -20,7 +20,6 @@ | |||
import * as React from 'react'; | |||
import { Component } from '../../../../types/types'; | |||
import CreateYmlFile from '../../components/CreateYmlFile'; | |||
import FinishButton from '../../components/FinishButton'; | |||
import GradleBuild from '../../components/GradleBuild'; | |||
import { GITHUB_ACTIONS_RUNS_ON_LINUX } from '../constants'; | |||
import { generateGitHubActionsYaml } from '../utils'; | |||
@@ -29,7 +28,6 @@ export interface GradleProps { | |||
branchesEnabled?: boolean; | |||
mainBranchName: string; | |||
component: Component; | |||
onDone: () => void; | |||
} | |||
const GRADLE_YAML_STEPS = ` | |||
@@ -71,7 +69,6 @@ export default function Gradle(props: GradleProps) { | |||
GRADLE_YAML_STEPS | |||
)} | |||
/> | |||
<FinishButton onClick={props.onDone} /> | |||
</> | |||
); | |||
} |
@@ -20,7 +20,6 @@ | |||
import * as React from 'react'; | |||
import { Component } from '../../../../types/types'; | |||
import CreateYmlFile from '../../components/CreateYmlFile'; | |||
import FinishButton from '../../components/FinishButton'; | |||
import { GITHUB_ACTIONS_RUNS_ON_LINUX } from '../constants'; | |||
import { generateGitHubActionsYaml } from '../utils'; | |||
@@ -28,7 +27,6 @@ export interface JavaMavenProps { | |||
branchesEnabled?: boolean; | |||
mainBranchName: string; | |||
component: Component; | |||
onDone: () => void; | |||
} | |||
function mavenYamlSteps(projectKey: string, projectName: string) { | |||
@@ -60,17 +58,14 @@ function mavenYamlSteps(projectKey: string, projectName: string) { | |||
export default function JavaMaven(props: JavaMavenProps) { | |||
const { component, branchesEnabled, mainBranchName } = props; | |||
return ( | |||
<> | |||
<CreateYmlFile | |||
yamlFileName=".github/workflows/build.yml" | |||
yamlTemplate={generateGitHubActionsYaml( | |||
mainBranchName, | |||
!!branchesEnabled, | |||
GITHUB_ACTIONS_RUNS_ON_LINUX, | |||
mavenYamlSteps(component.key, component.name) | |||
)} | |||
/> | |||
<FinishButton onClick={props.onDone} /> | |||
</> | |||
<CreateYmlFile | |||
yamlFileName=".github/workflows/build.yml" | |||
yamlTemplate={generateGitHubActionsYaml( | |||
mainBranchName, | |||
!!branchesEnabled, | |||
GITHUB_ACTIONS_RUNS_ON_LINUX, | |||
mavenYamlSteps(component.key, component.name) | |||
)} | |||
/> | |||
); | |||
} |
@@ -21,7 +21,6 @@ import * as React from 'react'; | |||
import { Component } from '../../../../types/types'; | |||
import CreateYmlFile from '../../components/CreateYmlFile'; | |||
import DefaultProjectKey from '../../components/DefaultProjectKey'; | |||
import FinishButton from '../../components/FinishButton'; | |||
import { GITHUB_ACTIONS_RUNS_ON_LINUX } from '../constants'; | |||
import { generateGitHubActionsYaml } from '../utils'; | |||
@@ -29,7 +28,6 @@ export interface OthersProps { | |||
branchesEnabled?: boolean; | |||
mainBranchName: string; | |||
component: Component; | |||
onDone: () => void; | |||
} | |||
function otherYamlSteps(branchesEnabled: boolean) { | |||
@@ -70,7 +68,6 @@ export default function Others(props: OthersProps) { | |||
otherYamlSteps(!!branchesEnabled) | |||
)} | |||
/> | |||
<FinishButton onClick={props.onDone} /> | |||
</> | |||
); | |||
} |
@@ -17,7 +17,6 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import userEvent from '@testing-library/user-event'; | |||
import React from 'react'; | |||
import UserTokensMock from '../../../../api/mocks/UserTokensMock'; | |||
@@ -80,7 +79,7 @@ const ui = { | |||
jenkinsStepTitle: byRole('heading', { | |||
name: 'onboarding.tutorial.with.jenkins.jenkinsfile.title', | |||
}), | |||
allSetSentence: byText('onboarding.tutorial.ci_outro.all_set.sentence'), | |||
allSetSentence: byText('onboarding.tutorial.ci_outro.done'), | |||
...getTutorialActionButtons(), | |||
...getTutorialBuildButtons(), | |||
}; | |||
@@ -139,23 +138,23 @@ it.each([AlmKeys.BitbucketCloud, AlmKeys.BitbucketServer, AlmKeys.GitHub, AlmKey | |||
// CFamilly | |||
await user.click(ui.cFamilyBuildButton.get()); | |||
expect(getCopyToClipboardValue()).toMatchSnapshot(`sonar-project.properties code`); | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(`sonar-project.properties code`); | |||
await user.click(ui.linuxButton.get()); | |||
expect(getCopyToClipboardValue(1)).toMatchSnapshot(`cfamily linux jenkinsfile`); | |||
expect(getCopyToClipboardValue()).toMatchSnapshot(`cfamily linux jenkinsfile`); | |||
await user.click(ui.windowsButton.get()); | |||
expect(getCopyToClipboardValue(1)).toMatchSnapshot(`cfamily windows jenkinsfile`); | |||
expect(getCopyToClipboardValue()).toMatchSnapshot(`cfamily windows jenkinsfile`); | |||
await user.click(ui.macosButton.get()); | |||
expect(getCopyToClipboardValue(1)).toMatchSnapshot(`cfamily macos jenkinsfile`); | |||
expect(getCopyToClipboardValue()).toMatchSnapshot(`cfamily macos jenkinsfile`); | |||
// Other | |||
await user.click(ui.otherBuildButton.get()); | |||
expect(getCopyToClipboardValue()).toMatchSnapshot( | |||
expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( | |||
`other build tools sonar-project.properties code` | |||
); | |||
expect(getCopyToClipboardValue(1)).toMatchSnapshot(`other build tools jenkinsfile`); | |||
expect(getCopyToClipboardValue()).toMatchSnapshot(`other build tools jenkinsfile`); | |||
await user.click(ui.finishTutorialButton.get()); | |||
expect(ui.allSetSentence.get()).toBeInTheDocument(); |
@@ -52,7 +52,7 @@ export function getCommonNodes(ci: TutorialModes) { | |||
ci === TutorialModes.GitHubActions ? 'secret' : 'variables' | |||
}.intro.link`, | |||
}), | |||
allSetSentence: byText('onboarding.tutorial.ci_outro.all_set.sentence'), | |||
allSetSentence: byText('onboarding.tutorial.ci_outro.done'), | |||
}; | |||
} | |||
@@ -0,0 +1,53 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { RefObject, useEffect, useState } from 'react'; | |||
import { isDefined } from '../helpers/types'; | |||
interface Options extends IntersectionObserverInit { | |||
freezeOnceVisible?: boolean; | |||
} | |||
export default function useIntersectionObserver<T extends Element>( | |||
ref: RefObject<T>, | |||
options: Options = {} | |||
) { | |||
const { root = null, rootMargin = '0px', threshold = 0, freezeOnceVisible = false } = options; | |||
const [entry, setEntry] = useState<IntersectionObserverEntry>(); | |||
const frozen = (entry?.isIntersecting || false) && freezeOnceVisible; | |||
useEffect(() => { | |||
if (!isDefined(IntersectionObserver) || !isDefined(ref.current) || frozen) { | |||
return; | |||
} | |||
const observer = new IntersectionObserver( | |||
([entry]) => setEntry(entry), | |||
{ root, rootMargin, threshold } | |||
); | |||
observer.observe(ref.current); | |||
return () => observer.disconnect(); | |||
}, [ref, frozen, root, rootMargin, threshold]); | |||
return entry; | |||
} |
@@ -3606,6 +3606,71 @@ __metadata: | |||
languageName: node | |||
linkType: hard | |||
"@react-spring/animated@npm:~9.7.3": | |||
version: 9.7.3 | |||
resolution: "@react-spring/animated@npm:9.7.3" | |||
dependencies: | |||
"@react-spring/shared": ~9.7.3 | |||
"@react-spring/types": ~9.7.3 | |||
peerDependencies: | |||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 | |||
checksum: 468942ca3a11c02c3e56def26b2da9dd10ddbed548004245c4ac309cce00b58d971e781abed67db0d652f72737eaa73766ea9a43b8ef3b08a7ed2eddc04d4c39 | |||
languageName: node | |||
linkType: hard | |||
"@react-spring/core@npm:~9.7.3": | |||
version: 9.7.3 | |||
resolution: "@react-spring/core@npm:9.7.3" | |||
dependencies: | |||
"@react-spring/animated": ~9.7.3 | |||
"@react-spring/shared": ~9.7.3 | |||
"@react-spring/types": ~9.7.3 | |||
peerDependencies: | |||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 | |||
checksum: 8a80a07276458fd14099320eda824e58a11ce3a9b03a5c9cd3f4252adb4d26da04ee5caf5cbc961199f55c2d58a99638d5ea292cdb6aa029208dbab741b5c531 | |||
languageName: node | |||
linkType: hard | |||
"@react-spring/rafz@npm:9.7.3": | |||
version: 9.7.3 | |||
resolution: "@react-spring/rafz@npm:9.7.3" | |||
checksum: c88b3c6306eab4a93a9f4ea38ad2e0ea3287a8b79a9a092cb1fd934a97ce092e41c32373b2b2e3de4b63027de7ae7414e0d442936dd8d94b51c2ff7d51e8f643 | |||
languageName: node | |||
linkType: hard | |||
"@react-spring/shared@npm:~9.7.3": | |||
version: 9.7.3 | |||
resolution: "@react-spring/shared@npm:9.7.3" | |||
dependencies: | |||
"@react-spring/types": ~9.7.3 | |||
peerDependencies: | |||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 | |||
checksum: 912b5e567eb5345c9a6c8e8c0c2d69b1f411af72a0685b95831809c267c89846a31341ca071f284ace98b3cb5de647054dc76f6ace81d6379513eaf96b52f195 | |||
languageName: node | |||
linkType: hard | |||
"@react-spring/types@npm:~9.7.3": | |||
version: 9.7.3 | |||
resolution: "@react-spring/types@npm:9.7.3" | |||
checksum: f47b81fe556464aa54a78603311cb584d6a0f03088522229afb058265bbe2ade2095a55ec7f4e960c3b9cceaa5d47865bc41fc6643c0f5f4bd3d8650203d8389 | |||
languageName: node | |||
linkType: hard | |||
"@react-spring/web@npm:9.7.3": | |||
version: 9.7.3 | |||
resolution: "@react-spring/web@npm:9.7.3" | |||
dependencies: | |||
"@react-spring/animated": ~9.7.3 | |||
"@react-spring/core": ~9.7.3 | |||
"@react-spring/shared": ~9.7.3 | |||
"@react-spring/types": ~9.7.3 | |||
peerDependencies: | |||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 | |||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 | |||
checksum: 7f5cd05b2314b7f2f715e1926abcf9aa0a539399b222ab34e989144f48350adfcd2edab65d41425570f72c57f602fc6994d6730fbeed902171ac527b630a8a9b | |||
languageName: node | |||
linkType: hard | |||
"@remix-run/router@npm:1.6.2": | |||
version: 1.6.2 | |||
resolution: "@remix-run/router@npm:1.6.2" | |||
@@ -4844,6 +4909,8 @@ __metadata: | |||
"@emotion/react": 11.11.1 | |||
"@emotion/styled": 11.11.0 | |||
"@primer/octicons-react": 19.3.0 | |||
"@react-spring/rafz": 9.7.3 | |||
"@react-spring/web": 9.7.3 | |||
"@swc/core": 1.3.65 | |||
"@swc/jest": 0.2.26 | |||
"@tanstack/react-query": 4.29.14 |
@@ -4058,8 +4058,8 @@ onboarding.analysis.dotnetcore.global.text.path=Make sure dotnet tools folder is | |||
onboarding.tutorial.return_to_list=Choose another option | |||
onboarding.tutorial.ci_outro.all_set.title=You're all set! | |||
onboarding.tutorial.ci_outro.all_set.sentence={all_set} and ready to improve the quality and security of your code! | |||
onboarding.tutorial.ci_outro.all_set.sentence.all_set=You're all set | |||
onboarding.tutorial.ci_outro.done=And you are done! | |||
onboarding.tutorial.ci_outro.refresh_text=If everything is running successfully, once the analysis is complete you'll be redirected to the Overview page of your project where the new analysis results will be displayed. This can take a few minutes. | |||
onboarding.tutorial.ci_outro.commit=Commit and push your code to start the analysis. | |||
onboarding.tutorial.ci_outro.commit.why.gitlab=Each new push you make on your branches or merge requests will trigger a new analysis in SonarQube. We will decorate merge requests directly on GitLab for you. | |||
onboarding.tutorial.ci_outro.commit.why.github=Each new push you make on your branches or pull requests will trigger a new analysis in SonarQube. We will decorate pull requests directly on GitHub for you. | |||
@@ -4069,7 +4069,6 @@ onboarding.tutorial.ci_outro.commit.why.azure=Each new push you make on your bra | |||
onboarding.tutorial.ci_outro.commit.why.no_branches=Each new push you make on your main branch will trigger a new analysis in SonarQube. | |||
onboarding.tutorial.ci_outro.refresh=This page will then refresh with your analysis results. | |||
onboarding.tutorial.ci_outro.refresh.why=If the page doesn't refresh after a while, please double-check the analysis configuration, and check your logs. | |||
onboarding.tutorial.ci_outro.waiting_for_fist_analysis=Waiting for the first analysis to come in... | |||
onboarding.tutorial.other.project_key.sentence=Create a {file} file in your repository and paste the following code: | |||
onboarding.tutorial.cfamilly.compilation_database_info=If you have trouble using the build wrapper, you can try using a {link}. | |||
onboarding.tutorial.cfamilly.compilation_database_info.link=compilation database |