From 677ca70eedf0dd82703ac1fd0243b9a2a6bf43ef Mon Sep 17 00:00:00 2001 From: Shaun Hamilton Date: Tue, 7 Oct 2025 23:27:57 +0200 Subject: [PATCH] feat(client): add 3 disclosure cde for rdb (#62178) Co-authored-by: Ahmad Abdolsaheb --- .../config/growthbook-features-default.json | 31 +- client/i18n/locales/english/translations.json | 26 +- .../codeally/codespaces-instructions.tsx | 190 ++++++++++ ...nstructions.tsx => local-instructions.tsx} | 105 +----- ...-instructions.tsx => ona-instructions.tsx} | 116 ++---- .../codeally/rdb-local-logout-alert.tsx | 8 +- .../templates/Challenges/codeally/show.tsx | 331 +++++++++--------- e2e/user-token.spec.ts | 2 +- 8 files changed, 445 insertions(+), 364 deletions(-) create mode 100644 client/src/templates/Challenges/codeally/codespaces-instructions.tsx rename client/src/templates/Challenges/codeally/{rdb-local-instructions.tsx => local-instructions.tsx} (58%) rename client/src/templates/Challenges/codeally/{rdb-ona-instructions.tsx => ona-instructions.tsx} (56%) diff --git a/client/config/growthbook-features-default.json b/client/config/growthbook-features-default.json index 048953fe1d6..5762bdc9e78 100644 --- a/client/config/growthbook-features-default.json +++ b/client/config/growthbook-features-default.json @@ -5,6 +5,15 @@ "aa-test-in-component": { "defaultValue": false }, + "rdb-codespaces-instructions": { + "defaultValue": true + }, + "rdb-local-instructions": { + "defaultValue": true + }, + "rdb-ona-instructions": { + "defaultValue": true + }, "landing-top-skill-focused": { "defaultValue": false, "rules": [ @@ -13,8 +22,14 @@ "hashAttribute": "id", "seed": "landing-top-skill-focused", "hashVersion": 2, - "variations": [false, true], - "weights": [0.5, 0.5], + "variations": [ + false, + true + ], + "weights": [ + 0.5, + 0.5 + ], "key": "landing-top-skill-focused", "meta": [ { @@ -31,7 +46,7 @@ } ] }, - "replace-20-with-25": { + "replace-20-with-25": { "defaultValue": false, "rules": [ { @@ -39,8 +54,14 @@ "hashAttribute": "id", "seed": "replace-20-with-25", "hashVersion": 2, - "variations": [false, true], - "weights": [0.5, 0.5], + "variations": [ + false, + true + ], + "weights": [ + 0.5, + 0.5 + ], "key": "replace-20-with-25", "meta": [ { diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index 7d11816fc46..a06bef8f4a9 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -533,6 +533,26 @@ "project-preview-title": "Here's a preview of what you will build", "demo-project-title": "Here's an example of a project that meets the requirements", "github-required": "<0>Create a GitHub account if you don't have one. You'll need it when you create the virtual Linux server machine. This process may take a few minutes.", + "codespaces": { + "intro": "This course runs in a virtual Linux machine using GitHub Codespaces. Follow these instructions to start the course:", + "step-1": "<0>Create an GitHub account if you don't have one", + "step-2": "Click the start button below", + "step-3": "On that page, click the create button", + "step-4": "Once the virtual Linux machine is finished loading, start the CodeRoad extension by:", + "step-5": "Clicking the \"hamburger\" menu near the top left of the VSCode window,", + "step-6": "Going to the <0>View menu,", + "step-7": "Clicking on the <0>Command Palette option,", + "step-8": "and running the <0>CodeRoad: Start command", + "step-9": "Follow the instructions in CodeRoad to complete the course", + "continue-project": "Clicking the button below will start a new project. If you have previously started the {{title}} course, go to the <0>repository page to re-open a previous workspace.", + "learn-more": "Learn more about <0>Codespace workspaces.", + "logout-warning": "If you log out of freeCodeCamp before you complete the entire {{course}} course, your progress will not be saved to your freeCodeCamp account.", + "sub-step-3": "Navigate to your <0>Codespaces secrets page", + "sub-step-4": "Create a new secret named <0>CODEROAD_WEBHOOK_TOKEN", + "sub-step-5": "In the <0>Value field, paste your token", + "sub-step-6": "In the <0>Repository access field, select the <1>freeCodeCamp/rdb-alpha repository", + "summary": "Codespaces Setup" + }, "ona": { "intro": "This course runs in a virtual Linux machine using Ona. Follow these instructions to start the course:", "step-1": "<0>Create an Ona account if you don't have one", @@ -549,7 +569,8 @@ "logout-warning": "If you log out of freeCodeCamp before you complete the entire {{course}} course, your progress will not be saved to your freeCodeCamp account.", "sub-step-3": "Navigate to your <0>Ona secrets page", "sub-step-4": "Create a new secret named <0>CODEROAD_WEBHOOK_TOKEN", - "sub-step-5": "In the <0>Secret field, paste your token" + "sub-step-5": "In the <0>Secret field, paste your token", + "summary": "Ona Setup" }, "local": { "intro": "This course runs in a virtual Linux machine on your computer. To run the course, you first need to download each of the following if you don't already have them:", @@ -571,7 +592,8 @@ "step-7": "Copy the course URL below, paste it in the URL input, and click \"Load\".", "copy-url": "Copy Course URL", "step-8": "Click \"Start\" to begin.", - "step-9": "Follow the instructions in CodeRoad to complete the course. Note: You may need to restart the terminal once for terminal settings to take effect and the tests to pass." + "step-9": "Follow the instructions in CodeRoad to complete the course. Note: You may need to restart the terminal once for terminal settings to take effect and the tests to pass.", + "summary": "Local Setup" }, "step-1": "Step 1: Complete the project", "step-2": "Step 2: Submit your code", diff --git a/client/src/templates/Challenges/codeally/codespaces-instructions.tsx b/client/src/templates/Challenges/codeally/codespaces-instructions.tsx new file mode 100644 index 00000000000..e102a609e58 --- /dev/null +++ b/client/src/templates/Challenges/codeally/codespaces-instructions.tsx @@ -0,0 +1,190 @@ +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { Spacer, Button, Callout } from '@freecodecamp/ui'; + +import { CodeAllyButton } from '../../../components/growth-book/codeally-button'; + +interface CodespacesInstructionsProps { + challengeType: number; + copyUrl: () => void; + copyUserToken: () => void; + generateUserToken: () => Promise; + isSignedIn: boolean; + title: string; + userToken: string | null; +} + +export function CodespacesInstructions({ + challengeType, + copyUrl, + copyUserToken, + generateUserToken, + isSignedIn, + title, + userToken +}: CodespacesInstructionsProps) { + const { t } = useTranslation(); + + function openCodespaces() { + const codespacesUrl = `https://codespaces.new/freeCodeCamp/rdb-alpha`; + + window.open(codespacesUrl, '_blank'); + } + + return ( +
+

{t('learn.codespaces.intro')}

+
    +
  1. + + + placeholder + + +
  2. + {isSignedIn && ( + <> + +

    {t('learn.local.sub-step-heading')}

    +
      +
    1. {t('learn.local.sub-step-1')}
    2. + + + +
    3. {t('learn.local.sub-step-2')}
    4. + + + +
    5. + + + Codespaces secrets page + + +
    6. +
    7. + + placeholder + +
    8. +
    9. + + placeholder + +
    10. +
    11. + + placeholder + placeholder + +
    12. +
    + + + )} +
  3. {t('learn.codespaces.step-2')}
  4. +
  5. {t('learn.codespaces.step-3')}
  6. +
  7. + {t('learn.codespaces.step-4')} +
      +
    • {t('learn.codespaces.step-5')}
    • +
    • + + placeholder + +
    • +
    • + + placeholder + +
    • +
    • + + placeholder + +
    • +
    • {t('learn.local.step-6')}
    • +
    • {t('learn.local.step-7')}
    • + + + +
    • {t('learn.local.step-8')}
    • +
    +
  8. +
  9. {t('learn.codespaces.step-9')}
  10. +
+ + + {isSignedIn && } + +
+ ); +} + +interface CodespacesContinueAlertProps { + title: string; +} + +function CodespacesContinueAlert({ title }: CodespacesContinueAlertProps) { + return ( + + + + placeholder + + + + + + placeholder + + + + ); +} + +interface CodespacesLogoutAlertProps { + course: string; +} + +function CodespacesLogoutAlert({ + course +}: CodespacesLogoutAlertProps): JSX.Element { + const { t } = useTranslation(); + + return ( + + {t('learn.codespaces.logout-warning', { course })} + + ); +} diff --git a/client/src/templates/Challenges/codeally/rdb-local-instructions.tsx b/client/src/templates/Challenges/codeally/local-instructions.tsx similarity index 58% rename from client/src/templates/Challenges/codeally/rdb-local-instructions.tsx rename to client/src/templates/Challenges/codeally/local-instructions.tsx index a1394f8d2d0..c9ef8372b0d 100644 --- a/client/src/templates/Challenges/codeally/rdb-local-instructions.tsx +++ b/client/src/templates/Challenges/codeally/local-instructions.tsx @@ -1,103 +1,29 @@ import React from 'react'; -import { connect } from 'react-redux'; import { Trans, useTranslation } from 'react-i18next'; import { Spacer, Button } from '@freecodecamp/ui'; -import { postUserToken } from '../../../utils/ajax'; -import { createFlashMessage } from '../../../components/Flash/redux'; -import { FlashMessages } from '../../../components/Flash/redux/flash-messages'; -import { - isSignedInSelector, - userTokenSelector -} from '../../../redux/selectors'; -import { updateUserToken } from '../../../redux/actions'; import { Link } from '../../../components/helpers'; - import RdbLocalLogoutAlert from './rdb-local-logout-alert'; -const mapStateToProps = (state: unknown) => ({ - isSignedIn: isSignedInSelector(state), - userToken: userTokenSelector(state) as string | null -}); - -const mapDispatchToProps = { - createFlashMessage, - updateUserToken -}; - -interface RdbLocalInstructionsProps { - course: string; - createFlashMessage: typeof createFlashMessage; +interface LocalInstructionsProps { + copyUrl: () => void; + copyUserToken: () => void; + generateUserToken: () => Promise; isSignedIn: boolean; - updateUserToken: (arg0: string) => void; - url: string; + title: string; userToken: string | null; } -function RdbLocalInstructions({ - course, - createFlashMessage, +export function LocalInstructions({ + copyUrl, + copyUserToken, + generateUserToken, isSignedIn, - updateUserToken, - url, + title, userToken -}: RdbLocalInstructionsProps): JSX.Element { +}: LocalInstructionsProps) { const { t } = useTranslation(); - const coderoadTutorial = `https://raw.githubusercontent.com/${url}/main/tutorial.json`; - - const generateUserToken = async () => { - const createUserTokenResponse = await postUserToken(); - const { data = { userToken: null } } = createUserTokenResponse; - - if (data?.userToken) { - updateUserToken(data.userToken); - createFlashMessage({ - type: 'success', - message: FlashMessages.UserTokenGenerated - }); - } else { - createFlashMessage({ - type: 'danger', - message: FlashMessages.UserTokenGenerateError - }); - } - }; - - const copyUserToken = () => { - navigator.clipboard.writeText(userToken ?? '').then( - () => { - createFlashMessage({ - type: 'success', - message: FlashMessages.UserTokenCopied - }); - }, - () => { - createFlashMessage({ - type: 'danger', - message: FlashMessages.UserTokenCopyError - }); - } - ); - }; - - const copyUrl = () => { - navigator.clipboard.writeText(coderoadTutorial ?? '').then( - () => { - createFlashMessage({ - type: 'success', - message: FlashMessages.CourseUrlCopied - }); - }, - () => { - createFlashMessage({ - type: 'danger', - message: FlashMessages.CourseUrlCopyError - }); - } - ); - }; - return (

{t('learn.local.intro')}

@@ -174,7 +100,7 @@ function RdbLocalInstructions({ - + @@ -203,10 +129,3 @@ function RdbLocalInstructions({
); } - -RdbLocalInstructions.displayName = 'RdbLocalInstructions'; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(RdbLocalInstructions); diff --git a/client/src/templates/Challenges/codeally/rdb-ona-instructions.tsx b/client/src/templates/Challenges/codeally/ona-instructions.tsx similarity index 56% rename from client/src/templates/Challenges/codeally/rdb-ona-instructions.tsx rename to client/src/templates/Challenges/codeally/ona-instructions.tsx index 2fecc89e750..115a0f40468 100644 --- a/client/src/templates/Challenges/codeally/rdb-ona-instructions.tsx +++ b/client/src/templates/Challenges/codeally/ona-instructions.tsx @@ -1,101 +1,40 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { connect } from 'react-redux'; import { Spacer, Button } from '@freecodecamp/ui'; -import { postUserToken } from '../../../utils/ajax'; -import { createFlashMessage } from '../../../components/Flash/redux'; -import { FlashMessages } from '../../../components/Flash/redux/flash-messages'; -import { - isSignedInSelector, - userTokenSelector -} from '../../../redux/selectors'; -import { updateUserToken } from '../../../redux/actions'; +import { CodeAllyButton } from '../../../components/growth-book/codeally-button'; -import RdbLocalLogoutAlert from './rdb-local-logout-alert'; +import RdbOnaContinueAlert from './rdb-ona-continue-alert'; +import RdbOnaLogoutAlert from './rdb-ona-logout-alert'; -const mapStateToProps = (state: unknown) => ({ - isSignedIn: isSignedInSelector(state), - userToken: userTokenSelector(state) as string | null -}); - -const mapDispatchToProps = { - createFlashMessage, - updateUserToken -}; - -interface RdbOnaInstructionsProps { - course: string; - createFlashMessage: typeof createFlashMessage; +interface OneInstructionsProps { + challengeType: number; + copyUrl: () => void; + copyUserToken: () => void; + generateUserToken: () => Promise; isSignedIn: boolean; - updateUserToken: (arg0: string) => void; - url: string; + title: string; userToken: string | null; } -function RdbOnaInstructions({ - course, - createFlashMessage, +export function OnaInstructions({ + challengeType, + copyUrl, + copyUserToken, + generateUserToken, isSignedIn, - updateUserToken, - url, + title, userToken -}: RdbOnaInstructionsProps): JSX.Element { +}: OneInstructionsProps) { const { t } = useTranslation(); - const coderoadTutorial = `https://raw.githubusercontent.com/${url}/main/tutorial.json`; + function openOna() { + const repoUrl = `https://github.com/freeCodeCamp/rdb-alpha`; + const onaDomain = `https://app.ona.com/`; + const onaUrl = `${onaDomain}#${repoUrl}`; - const generateUserToken = async () => { - const createUserTokenResponse = await postUserToken(); - const { data = { userToken: null } } = createUserTokenResponse; - - if (data?.userToken) { - updateUserToken(data.userToken); - createFlashMessage({ - type: 'success', - message: FlashMessages.UserTokenGenerated - }); - } else { - createFlashMessage({ - type: 'danger', - message: FlashMessages.UserTokenGenerateError - }); - } - }; - - const copyUserToken = () => { - navigator.clipboard.writeText(userToken ?? '').then( - () => { - createFlashMessage({ - type: 'success', - message: FlashMessages.UserTokenCopied - }); - }, - () => { - createFlashMessage({ - type: 'danger', - message: FlashMessages.UserTokenCopyError - }); - } - ); - }; - - const copyUrl = () => { - navigator.clipboard.writeText(coderoadTutorial ?? '').then( - () => { - createFlashMessage({ - type: 'success', - message: FlashMessages.CourseUrlCopied - }); - }, - () => { - createFlashMessage({ - type: 'danger', - message: FlashMessages.CourseUrlCopyError - }); - } - ); - }; + window.open(onaUrl, '_blank'); + } return (
@@ -158,8 +97,6 @@ function RdbOnaInstructions({ placeholder - - @@ -185,6 +122,7 @@ function RdbOnaInstructions({ placeholder +
  • {t('learn.local.step-6')}
  • {t('learn.local.step-7')}
  • ); } - -RdbOnaInstructions.displayName = 'RdbOnaInstructions'; - -export default connect(mapStateToProps, mapDispatchToProps)(RdbOnaInstructions); diff --git a/client/src/templates/Challenges/codeally/rdb-local-logout-alert.tsx b/client/src/templates/Challenges/codeally/rdb-local-logout-alert.tsx index d3502ba183b..ac6bb40c12c 100644 --- a/client/src/templates/Challenges/codeally/rdb-local-logout-alert.tsx +++ b/client/src/templates/Challenges/codeally/rdb-local-logout-alert.tsx @@ -3,17 +3,15 @@ import { useTranslation } from 'react-i18next'; import { Callout } from '@freecodecamp/ui'; interface RdbLocalLogoutAlertProps { - course: string; + title: string; } -function RdbLocalLogoutAlert({ - course -}: RdbLocalLogoutAlertProps): JSX.Element { +function RdbLocalLogoutAlert({ title }: RdbLocalLogoutAlertProps): JSX.Element { const { t } = useTranslation(); return ( - {t('learn.local.logout-warning', { course })} + {t('learn.local.logout-warning', { course: title })} ); } diff --git a/client/src/templates/Challenges/codeally/show.tsx b/client/src/templates/Challenges/codeally/show.tsx index 745a24c666c..16026f62410 100644 --- a/client/src/templates/Challenges/codeally/show.tsx +++ b/client/src/templates/Challenges/codeally/show.tsx @@ -1,6 +1,6 @@ // Package Utilities import { graphql } from 'gatsby'; -import React, { useEffect, useRef } from 'react'; +import React, { Fragment, useEffect, useRef } from 'react'; import Helmet from 'react-helmet'; import type { TFunction } from 'i18next'; import { withTranslation } from 'react-i18next'; @@ -48,16 +48,13 @@ import { FlashMessages } from '../../../components/Flash/redux/flash-messages'; import { SuperBlocks } from '../../../../../shared-dist/config/curriculum'; import { CodeAllyDown } from '../../../components/growth-book/codeally-down'; import { postUserToken } from '../../../utils/ajax'; -import { CodeAllyButton } from '../../../components/growth-book/codeally-button'; - -import RdbOnaContinueAlert from './rdb-ona-continue-alert'; -import RdbOnaInstructions from './rdb-ona-instructions'; -import RdbOnaLogoutAlert from './rdb-ona-logout-alert'; -import RdbLocalInstructions from './rdb-local-instructions'; import RdbStep1Instructions from './rdb-step-1-instructions'; import RdbStep2Instructions from './rdb-step-2-instructions'; +import { LocalInstructions } from './local-instructions'; +import { OnaInstructions } from './ona-instructions'; import './codeally.css'; +import { CodespacesInstructions } from './codespaces-instructions'; // Redux const mapStateToProps = createSelector( @@ -116,33 +113,43 @@ interface ShowCodeAllyProps { userToken: string | null; } -function ShowCodeAlly(props: ShowCodeAllyProps) { +function ShowCodeAlly({ + completedChallenges, + data, + isChallengeCompleted, + isSignedIn, + partiallyCompletedChallenges, + t, + updateSolutionFormValues, + userToken, + updateUserToken, + createFlashMessage, + challengeMounted, + initTests, + pageContext: { challengeMeta }, + updateChallengeMeta, + openCompletionModal +}: ShowCodeAllyProps) { const container = useRef(null); const { - completedChallenges, - data: { - challengeNode: { - challenge: { - block, - challengeType, - description, - id: challengeId, - instructions, - notes, - superBlock, - title, - translationPending, - url - } + challengeNode: { + challenge: { + block, + challengeType, + fields: { tests }, + description, + helpCategory, + id: challengeId, + instructions, + notes, + superBlock, + title, + translationPending, + url } - }, - isChallengeCompleted, - isSignedIn, - partiallyCompletedChallenges, - t, - updateSolutionFormValues - } = props; + } + } = data; const blockNameTitle = `${t( `intro:${superBlock}.blocks.${block}.title` @@ -158,22 +165,6 @@ function ShowCodeAlly(props: ShowCodeAllyProps) { ); useEffect(() => { - const { - challengeMounted, - data: { - challengeNode: { - challenge: { - fields: { tests }, - challengeType, - helpCategory, - title - } - } - }, - pageContext: { challengeMeta }, - initTests, - updateChallengeMeta - } = props; initTests(tests); const challengePaths = getChallengePaths({ currentCurriculumPaths: challengeMeta @@ -191,54 +182,11 @@ function ShowCodeAlly(props: ShowCodeAllyProps) { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - function openOna() { - const repoUrl = `https://github.com/freeCodeCamp/rdb-alpha`; - const onaDomain = `https://app.ona.com/`; - const onaUrl = `${onaDomain}#${repoUrl}`; - - window.open(onaUrl, '_blank'); - } - - const startCourse = async () => { - const { isSignedIn, userToken, updateUserToken } = props; - - if (!isSignedIn) { - openOna(); - } else if (!userToken) { - const createUserTokenResponse = await postUserToken(); - const { data = { userToken: null } } = createUserTokenResponse; - - if (data?.userToken) { - updateUserToken(data.userToken); - openOna(); - } else { - createFlashMessage({ - type: 'danger', - message: FlashMessages.StartProjectErr - }); - } - } else { - openOna(); - } - }; - const handleSubmit = ({ showCompletionModal }: { showCompletionModal: boolean; }) => { - const { - completedChallenges, - createFlashMessage, - data: { - challengeNode: { - challenge: { id: challengeId } - } - }, - openCompletionModal, - partiallyCompletedChallenges - } = props; - const isPartiallyCompleted = partiallyCompletedChallenges.some( challenge => challenge.id === challengeId ); @@ -257,7 +205,85 @@ function ShowCodeAlly(props: ShowCodeAllyProps) { } }; - const onaDeprecated = useFeature('gitpod-deprecated').on; + const rdbLocalInstructions = useFeature('rdb-local-instructions'); + const rdbCodespacesInstructions = useFeature('rdb-codespaces-instructions'); + const rdbOnaInstructions = useFeature('rdb-ona-instructions'); + + const coderoadTutorial = `https://raw.githubusercontent.com/${url}/main/tutorial.json`; + + async function generateUserToken() { + const createUserTokenResponse = await postUserToken(); + const { data = { userToken: null } } = createUserTokenResponse; + + if (data?.userToken) { + updateUserToken(data.userToken); + createFlashMessage({ + type: 'success', + message: FlashMessages.UserTokenGenerated + }); + } else { + createFlashMessage({ + type: 'danger', + message: FlashMessages.UserTokenGenerateError + }); + } + } + + function copyUserToken() { + navigator.clipboard.writeText(userToken ?? '').then( + () => { + createFlashMessage({ + type: 'success', + message: FlashMessages.UserTokenCopied + }); + }, + () => { + createFlashMessage({ + type: 'danger', + message: FlashMessages.UserTokenCopyError + }); + } + ); + } + + function copyUrl() { + navigator.clipboard.writeText(coderoadTutorial ?? '').then( + () => { + createFlashMessage({ + type: 'success', + message: FlashMessages.CourseUrlCopied + }); + }, + () => { + createFlashMessage({ + type: 'danger', + message: FlashMessages.CourseUrlCopyError + }); + } + ); + } + + const setups = [ + { + name: t('learn.codespaces.summary'), + component: CodespacesInstructions, + on: rdbCodespacesInstructions.on + }, + { + name: t('learn.local.summary'), + component: LocalInstructions, + on: rdbLocalInstructions.on + }, + { + name: t('learn.ona.summary'), + component: OnaInstructions, + on: rdbOnaInstructions.on + } + ]; + + const setupsToShow = setups.filter(setup => { + return setup.on; + }); return ( @@ -279,88 +305,55 @@ function ShowCodeAlly(props: ShowCodeAllyProps) { - {onaDeprecated ? ( + {setupsToShow.map(({ name, component: SetupComponent }, i) => ( + +
    + {name} + + +
    + +
    + ))} + + + {isSignedIn && challengeType === challengeTypes.codeAllyCert && ( <> - +
    + {t('learn.complete-both-steps')} +
    +
    - {isSignedIn && - challengeType === challengeTypes.codeAllyCert && ( - <> -
    - {t('learn.complete-both-steps')} -
    -
    - - -
    - - - - - - )} - - ) : ( - <> - + +
    - {isSignedIn && - challengeType === challengeTypes.codeAllyCert ? ( - <> -
    - {t('learn.complete-both-steps')} -
    -
    - - - - - {isSignedIn && } - -
    - - - - - - ) : ( - <> - - {isSignedIn && } - - - )} - + + + )} diff --git a/e2e/user-token.spec.ts b/e2e/user-token.spec.ts index 88cf267cd0a..6e7c03f9efe 100644 --- a/e2e/user-token.spec.ts +++ b/e2e/user-token.spec.ts @@ -28,7 +28,7 @@ test.describe('After creating token', () => { await page.goto( '/learn/relational-database/learn-bash-by-building-a-boilerplate/build-a-boilerplate' ); - await page.getByRole('button', { name: 'Start the course' }).click(); + await page.getByText('Generate User Token').first().click(); await page.goto('/settings'); // Set `exact` to `true` to only match the panel heading