Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: test express app #132

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ CONTRIBUTING.md
docs
.next
.cache
*.hbs
1 change: 1 addition & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { dirname, join } from "path"

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0

Expand Down
7 changes: 2 additions & 5 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { ThemeProvider, IntlProvider, locales } from "@ory/elements"
import { StoryFn } from "@storybook/react"

import "@ory/elements/assets/normalize.css"

import "@ory/elements/assets/fa-brands.min.css"
import "@ory/elements/assets/fa-solid.min.css"
import "@ory/elements/assets/fontawesome.min.css"
import "@ory/elements/assets/inter-font.css"
import "@ory/elements/assets/jetbrains-mono-font.css"
import "@ory/elements/assets/normalize.css"
import "@ory/elements/style.css"

import { StoryFn } from "@storybook/react"
import React from "react"

export const globalTypes = {
Expand Down
12 changes: 4 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,14 @@ individually.
```tsx
// Ory Elements
// optional global css reset
import "@ory/elements/assets/normalize.css"
// optional fontawesome icons
import "@ory/elements/assets/fa-brands.min.css"
import "@ory/elements/assets/fa-solid.min.css"
import "@ory/elements/assets/fontawesome.min.css"

// optional fonts
import "@ory/elements/assets/inter-font.css"
import "@ory/elements/assets/jetbrains-mono-font.css"

import "@ory/elements/assets/normalize.css"
// required styles for Ory Elements
import "@ory/elements/style.css"
```
Expand Down Expand Up @@ -302,16 +300,14 @@ Inside our components we provide the `<ThemeProvider />` which exposes the
```tsx
// Ory Elements
// optional global css reset
import "@ory/elements-preact/assets/normalize.css"
// optional fontawesome icons
import "@ory/elements-preact/assets/fa-brands.min.css"
import "@ory/elements-preact/assets/fa-solid.min.css"
import "@ory/elements-preact/assets/fontawesome.min.css"

// optional fonts
import "@ory/elements-preact/assets/inter-font.css"
import "@ory/elements-preact/assets/jetbrains-mono-font.css"

import "@ory/elements-preact/assets/normalize.css"
// required styles for Ory Elements
import "@ory/elements-preact/style.css"

Expand Down Expand Up @@ -382,9 +378,9 @@ For Express.js the library also exports a helper function which registers all
the CSS the library produces.

```ts
import express, { Application } from "express"
import { assignInlineVars } from "@vanilla-extract/dynamic"
import { oryTheme, Theme } from "../theme"
import { assignInlineVars } from "@vanilla-extract/dynamic"
import express, { Application } from "express"

export const RegisterOryElementsExpress = (app: Application, theme: Theme) => {
app.use("/theme.css", (req, res) => {
Expand Down
5 changes: 5 additions & 0 deletions examples/express/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/node_modules/
/lib/
/test-results/
/playwright-report/
/tests-out/
106 changes: 106 additions & 0 deletions examples/express/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Ory Elements

Ory Elements is a component library that makes building login, registration and
account pages for Ory a breeze.

- Reduces time to add complex auth flows to your customer experience, including
multi-factor authentication and account recovery
- Themeable and modular - use only what you need from it
- Works with the React ecosystem (NextJS, React SPA, Preact SPA)
- Works with the Express based ecosystem
- Dynamically adapts the user interface to your Ory identity schema, sign-in and
flow configuration

Ory Elements supports integrating with:

- React
- Preact
- ExpressJs (experimental)

## Get Started

```shell
git clone --depth 1 git@github.com:ory/elements.git
cd elements
npm run initialize
cd examples/express
export ORY_SDK_URL=http://localhost:4000
npm run dev
```

Now run the [Ory CLI](https://www.ory.sh/docs/guides/cli/installation) tunnel.

```shell
ory tunnel http://localhost:3000 --project <project-slug> --dev
```

The tunnel will now _mirror_ the Ory APIs under `http://localhost:4000` which we
have explicity told our Express.JS app to use through the `ORY_SDK_URL` export.

Now you can see Ory Elements in action by opening <http://localhost:3000> in
your browser!

### Configurations

- `ORY_SDK_URL` (required): The URL where ORY Kratos's Public API is located at.
If this app and ORY Kratos are running in the same private network, this
should be the private network address (e.g.
`kratos-public.svc.cluster.local`).
- `TLS_CERT_PATH` (optional): Path to certificate file. Should be set up
together with `TLS_KEY_PATH` to enable HTTPS.
- `TLS_KEY_PATH` (optional): Path to key file Should be set up together with
`TLS_CERT_PATH` to enable HTTPS.

This is the easiest mode as it requires no additional set up. This app runs on
port `:3000`.

### Base Path

There are two ways of serving this application under a base path:

1. Let Express.js handle the routing by setting the `BASE_PATH` environment
variable to the sub-path, e.g. `/myapp`.
2. Use a reverse proxy or API gateway to strip the path prefix.

The second approach is not always possible, especially when running the
application on a serverless environment. In this case, the first approach is
recommended.

## Development

To run this app with dummy data and no real connection to the Ory Network, use:

```shell script
NODE_ENV=stub npm start
```

### Using and Modifying the Example

If you want to re-use this example in your own project, you can do so by
installing the dependencies through NPM instead of using the latest build from
master.

```shell
cp -r examples/express <your-project>
cd <your-project>
npm i @ory/elements-markup @ory/client @ory/integrtions
```

Nothing else is required. You can now start modifying the example to your needs.

### End-to-End Testing

This example comes with a set of end-to-end tests that can be run with
`npm run test`. The tests are written with [Playwright](https://playwright.dev/)
and can be found in the `e2e` directory.

To use the tests, you need to have [Playwright](https://playwright.dev/) and
`@ory/elements-test` installed as a dev dependency.

For more details, see [End-to-End Testing](../../packages/test/README.md).

### Contributing

Found a bug or want to add a new feature? Please fork this repository and create
a pull request. If your changes are large, please open an issue first. This
application can be configured using two environment variables:
40 changes: 40 additions & 0 deletions examples/express/e2e/login.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { LoginMocks, LoginPage, test as base, UUIDv4 } from "@ory/elements-test"

const test = base.extend<{ loginPage: LoginPage }>({
loginPage: async ({ page, environment }, use) => {
const { applicationUrl, oryProjectUrl } = environment
const loginPage = new LoginPage(page, applicationUrl, oryProjectUrl, {
ssr: true,
})
await use(loginPage)
},
})

test.describe.parallel("Login Page", () => {
test.beforeEach(async ({ loginPage }) => {
loginPage.server?.listen()
})
test.afterEach(async ({ loginPage }) => {
loginPage.server?.close()
})

test.only("login success", async ({ loginPage }) => {
// we need to register a mock redirect
// for the express application to work
await loginPage.page.route("**/login", async (route) => {
await route.fulfill({
status: 303,
headers: {
Location: loginPage.pageUrl + "?flow=" + UUIDv4(),
},
})
})
await LoginMocks.LoginSuccessTest(loginPage)
})

test("login with invalid credentials", async ({ loginPage }) => {
await LoginMocks.LoginInvalidLoginCredentialsTest(loginPage)
})
})
14 changes: 14 additions & 0 deletions examples/express/e2e/recovery.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { RecoveryMocks, RecoveryPage, test } from "@ory/elements-test"

test.describe.parallel("Recovery Page", () => {
test("recovery success", async ({ environment, page }) => {
const { applicationUrl, oryProjectUrl } = environment
const recoveryPage = new RecoveryPage(page, applicationUrl, oryProjectUrl, {
ssr: true,
})

await RecoveryMocks.RecoverySuccessTest(recoveryPage)
})
})
35 changes: 35 additions & 0 deletions examples/express/e2e/registration.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { RegistrationMocks, RegistrationPage, test } from "@ory/elements-test"

test.describe.parallel("Registration Page", () => {
test("registration success", async ({ environment, page }) => {
const { applicationUrl, oryProjectUrl } = environment

const registrationPage = new RegistrationPage(
page,
applicationUrl,
oryProjectUrl,
{
ssr: true,
},
)

await RegistrationMocks.RegistrationSuccessTest(registrationPage)
})

test("registration duplicate account", async ({ environment, page }) => {
const { applicationUrl, oryProjectUrl } = environment

const registrationPage = new RegistrationPage(
page,
applicationUrl,
oryProjectUrl,
{
ssr: true,
},
)

await RegistrationMocks.RegistrationDuplicateAccountTest(registrationPage)
})
})
18 changes: 18 additions & 0 deletions examples/express/e2e/verification.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { test, VerificationMocks, VerificationPage } from "@ory/elements-test"

test.describe.parallel("Verification Page", () => {
test("verification success", async ({ environment, page }) => {
const { applicationUrl, oryProjectUrl } = environment
const verificationPage = new VerificationPage(
page,
applicationUrl,
oryProjectUrl,
{
ssr: true,
},
)
await VerificationMocks.VerificationSuccessTest(verificationPage)
})
})
70 changes: 70 additions & 0 deletions examples/express/global-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { spawn } from "child_process"
import path from "path"
import { RemoteHttpResolver } from "@mswjs/interceptors/RemoteHttpInterceptor"

const globlSetup = async () => {
process.env.APPLICATION_URL =
process.env.APPLICATION_URL || "http://localhost:3200"

process.env.ORY_PROJECT_URL =
process.env.ORY_PROJECT_URL || "http://localhost:4000"

process.env.ORY_PROJECT_API_TOKEN = ""

// const parentDirectory = path.join(__dirname, "../../")
// console.log("using parent directory", parentDirectory)
//
// // set env var for express server
// const nodemon = spawn("ts-node", ["./src/index.ts"], {
// cwd: __dirname,
// stdio: ["inherit", "inherit", "inherit", "ipc"],
// env: {
// ORY_SDK_URL: "http://localhost:4000",
// PORT: "3200",
// PATH: process.env.PATH,
// MOCK_SERVER: "true",
// },
// })
//
// const resolver = new RemoteHttpResolver({
// process: nodemon,
// })
//
// resolver.on('request', (req) => {
// console.log('request', req)
// })
//
// nodemon.stdout?.on("data", (data) => {
// console.log(`stdout: ${data}`)
// })
//
// nodemon.stderr?.on("data", (data) => {
// console.log(`stderr: ${data}`)
// })
//
// nodemon.on("error", (err) => {
// console.error(`nodemon encountered an error ${err}`)
// })
//
// nodemon.on("close", (code) => {
// console.log(`child process exited with code ${code}`)
// })
//
// let count = 10
// while (count > 0) {
// try {
// const resp = await fetch("http://localhost:3200")
// if (resp.ok) {
// break
// }
// } catch (e) {
// console.log("Trying to connect to service ", e)
// }
// count--
// await new Promise((resolve) => setTimeout(resolve, 1000))
// }
}

export default globlSetup
Loading
Loading