diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..67d2ffed --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v20.13.1 \ No newline at end of file diff --git a/__test__/common/utils/makeFullRepositoryName.test.ts b/__test__/common/utils/makeFullRepositoryName.test.ts new file mode 100644 index 00000000..b172452d --- /dev/null +++ b/__test__/common/utils/makeFullRepositoryName.test.ts @@ -0,0 +1,51 @@ +import { makeFullRepositoryName } from "@/common/utils/makeFullRepositoryName"; + +test("It returns a sanitized repository name with a suffix", async () => { + const result = makeFullRepositoryName({ name: "My Project", suffix: "-repo" }); + expect(result).toEqual("myproject-repo"); +}); + +test("It returns a lowercase repository name", async () => { + const result = makeFullRepositoryName({ name: "MyProject", suffix: "-repo" }); + expect(result).toEqual("myproject-repo"); +}); + +test("It trims spaces from the name", async () => { + const result = makeFullRepositoryName({ name: " My Project ", suffix: "-repo" }); + expect(result).toEqual("myproject-repo"); +}); + +test("It removes non-alphanumeric characters except dashes", async () => { + const result = makeFullRepositoryName({ name: "My!@#Pro$%^ject", suffix: "-repo" }); + expect(result).toEqual("myproject-repo"); +}); + +test("It replaces spaces with dashes", async () => { + const result = makeFullRepositoryName({ name: "My Project", suffix: "-repo" }); + expect(result).toEqual("myproject-repo"); +}); + +test("It handles names with multiple spaces correctly", async () => { + const result = makeFullRepositoryName({ name: "My Project", suffix: "-repo" }); + expect(result).toEqual("myproject-repo"); +}); + +test("It handles names with no characters left after sanitization", async () => { + const result = makeFullRepositoryName({ name: "!@#$%^", suffix: "-repo" }); + expect(result).toEqual("-repo"); +}); + +test("It handles names that are already safe", async () => { + const result = makeFullRepositoryName({ name: "safe-project-name", suffix: "-repo" }); + expect(result).toEqual("safe-project-name-repo"); +}); + +test("It returns just the suffix if the name is an empty string", async () => { + const result = makeFullRepositoryName({ name: "", suffix: "-repo" }); + expect(result).toEqual("-repo"); +}); + +test("It handles a suffix with special characters correctly", async () => { + const result = makeFullRepositoryName({ name: "My Project", suffix: "!-repo" }); + expect(result).toEqual("myproject!-repo"); +}); diff --git a/__test__/common/utils/makeNewGitHubRepositoryLink.test.ts b/__test__/common/utils/makeNewGitHubRepositoryLink.test.ts new file mode 100644 index 00000000..f88a3d6a --- /dev/null +++ b/__test__/common/utils/makeNewGitHubRepositoryLink.test.ts @@ -0,0 +1,64 @@ +import { makeNewGitHubRepositoryLink } from "@/common/utils/makeNewGitHubRepositoryLink"; + +test("It generates a URL with repository name and description", async () => { + const result = makeNewGitHubRepositoryLink({ + repositoryName: "test-repo", + description: "A test repository" + }); + expect(result).toEqual( + "https://github.com/new?name=test-repo&description=A%20test%20repository&visibility=private" + ); +}); + +test("It encodes special characters in the repository name and description", async () => { + const result = makeNewGitHubRepositoryLink({ + repositoryName: "test repo", + description: "A test & description" + }); + expect(result).toEqual( + "https://github.com/new?name=test%20repo&description=A%20test%20%26%20description&visibility=private" + ); +}); + +test("It generates a URL with a template repository", async () => { + const result = makeNewGitHubRepositoryLink({ + templateName: "owner/template-repo", + repositoryName: "test-repo", + description: "A test repository" + }); + expect(result).toEqual( + "https://github.com/new?name=test-repo&description=A%20test%20repository&visibility=private&template_owner=owner&template_name=template-repo" + ); +}); + +test("It handles template names with special characters", async () => { + const result = makeNewGitHubRepositoryLink({ + templateName: "owner/template repo", + repositoryName: "test-repo", + description: "A test repository" + }); + expect(result).toEqual( + "https://github.com/new?name=test-repo&description=A%20test%20repository&visibility=private&template_owner=owner&template_name=template%20repo" + ); +}); + +test("It returns the URL without template parameters if templateName is not provided", async () => { + const result = makeNewGitHubRepositoryLink({ + repositoryName: "test-repo", + description: "A test repository" + }); + expect(result).toEqual( + "https://github.com/new?name=test-repo&description=A%20test%20repository&visibility=private" + ); +}); + +test("It returns a URL even if the template name cannot be split into owner and repository", async () => { + const result = makeNewGitHubRepositoryLink({ + templateName: "invalidTemplateName", + repositoryName: "test-repo", + description: "A test repository" + }); + expect(result).toEqual( + "https://github.com/new?name=test-repo&description=A%20test%20repository&visibility=private" + ); +}); diff --git a/src/app/(authed)/(home)/[[...slug]]/layout.tsx b/src/app/(authed)/(home)/[[...slug]]/layout.tsx index 36fb09c1..2e89f12a 100644 --- a/src/app/(authed)/(home)/[[...slug]]/layout.tsx +++ b/src/app/(authed)/(home)/[[...slug]]/layout.tsx @@ -12,7 +12,7 @@ export default function Page({ children }: { children: React.ReactNode }) { } return ( <> - > + }>
diff --git a/src/app/(authed)/(home)/new/layout.tsx b/src/app/(authed)/(home)/new/layout.tsx new file mode 100644 index 00000000..4a2d7f6a --- /dev/null +++ b/src/app/(authed)/(home)/new/layout.tsx @@ -0,0 +1,23 @@ +"use client" + +import SecondarySplitHeader from "@/features/sidebar/view/SecondarySplitHeader" +import { Box } from "@mui/material" + +export default function Page({ children }: { children: React.ReactNode }) { + return ( + <> + + + + + {children} + + + ) +} \ No newline at end of file diff --git a/src/app/(authed)/(home)/new/page.tsx b/src/app/(authed)/(home)/new/page.tsx index bdbbd356..0d6def5d 100644 --- a/src/app/(authed)/(home)/new/page.tsx +++ b/src/app/(authed)/(home)/new/page.tsx @@ -1,56 +1,17 @@ -import Link from "next/link" import { env, splitOwnerAndRepository } from "@/common" +import NewProject from "@/features/new-project/NewProject"; const Page = () => { const repositoryNameSuffix = env.getOrThrow("REPOSITORY_NAME_SUFFIX") const templateName = env.get("NEW_PROJECT_TEMPLATE_REPOSITORY") - const projectName = "Nordisk Film" - const suffixedRepositoryName = makeFullRepositoryName({ - name: projectName, - suffix: repositoryNameSuffix - }) - const newGitHubRepositoryLink = makeNewGitHubRepositoryLink({ - templateName, - repositoryName: suffixedRepositoryName, - description: `Contains OpenAPI specifications for ${projectName}` - }) - return ( - - {newGitHubRepositoryLink} - - ) -} + const ownerRepository = templateName ? splitOwnerAndRepository(templateName)?.owner : undefined -export default Page - -function makeFullRepositoryName({ name, suffix }: { name: string, suffix: string }) { - const safeRepositoryName = name - .trim() - .toLowerCase() - .replace(/[^a-z0-9-]+/g, "") - .replace(/\s+/g, "-") - return `${safeRepositoryName}${suffix}` -} +return ( + +)} -function makeNewGitHubRepositoryLink({ - templateName, - repositoryName, - description -}: { - templateName?: string, - repositoryName: string, - description: string -}) { - let url = `https://github.com/new` - + `?name=${encodeURIComponent(repositoryName)}` - + `&description=${encodeURIComponent(description)}` - + `&visibility=private` - if (templateName) { - const templateRepository = splitOwnerAndRepository(templateName) - if (templateRepository) { - url += `&template_owner=${encodeURIComponent(templateRepository.owner)}` - url += `&template_name=${encodeURIComponent(templateRepository.repository)}` - } - } - return url -} +export default Page diff --git a/src/app/auth/signin/page.tsx b/src/app/auth/signin/page.tsx index cf08105e..af252ce6 100644 --- a/src/app/auth/signin/page.tsx +++ b/src/app/auth/signin/page.tsx @@ -1,11 +1,11 @@ import Image from "next/image" -import Link from "next/link" import { Box, Button, Stack, Typography } from "@mui/material" import { signIn } from "@/composition" import { env } from "@/common" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { faGithub } from "@fortawesome/free-brands-svg-icons" import SignInTexts from "@/features/auth/view/SignInTexts" +import MessageLinkFooter from "@/common/ui/MessageLinkFooter" const SITE_NAME = env.getOrThrow("NEXT_PUBLIC_SHAPE_DOCS_TITLE") const HELP_URL = env.get("NEXT_PUBLIC_SHAPE_DOCS_HELP_URL") @@ -74,9 +74,14 @@ const SignInColumn = () => { - -