Compare commits
29 Commits
v2.0.0-bet
...
@blitzjs/n
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13c2642bdb | ||
|
|
03bad3175d | ||
|
|
74a14b7040 | ||
|
|
6ece096134 | ||
|
|
a0596279bd | ||
|
|
fe2e4eb1e9 | ||
|
|
8c247e26e7 | ||
|
|
650a157e1d | ||
|
|
9dc81fe958 | ||
|
|
474b5494ac | ||
|
|
b33db08286 | ||
|
|
c1e0040639 | ||
|
|
5a7e6e0bb8 | ||
|
|
a6f32d1d0b | ||
|
|
c126b8191b | ||
|
|
775004f23e | ||
|
|
696f48c4e8 | ||
|
|
942536d9ad | ||
|
|
b493c93f8b | ||
|
|
b80c3d92ca | ||
|
|
4c0024c468 | ||
|
|
72a4e594ae | ||
|
|
62212bcc78 | ||
|
|
ed2b0e22c5 | ||
|
|
d250346d8b | ||
|
|
c0a3b1ee36 | ||
|
|
aaed9caa82 | ||
|
|
0020db295d | ||
|
|
9fd032106e |
@@ -2314,7 +2314,9 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/32314?v=4",
|
||||
"profile": "https://macwright.com/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
"doc",
|
||||
"test",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2911,7 +2913,8 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/120626?v=4",
|
||||
"profile": "https://github.com/medihack",
|
||||
"contributions": [
|
||||
"doc"
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3742,6 +3745,36 @@
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "paulm17",
|
||||
"name": "Paul",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/387463?v=4",
|
||||
"profile": "https://github.com/paulm17",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "selcukfatihsevinc",
|
||||
"name": "Selçuk Fatih Sevinç",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/384836?v=4",
|
||||
"profile": "https://github.com/selcukfatihsevinc",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "usamaster",
|
||||
"name": "usamaster",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5255330?v=4",
|
||||
"profile": "https://github.com/usamaster",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
5
.changeset/brave-zebras-deny.md
Normal file
5
.changeset/brave-zebras-deny.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/rpc": minor
|
||||
---
|
||||
|
||||
internal: remove unneeed useSession from useQuery hooks
|
||||
8
.changeset/calm-books-push.md
Normal file
8
.changeset/calm-books-push.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"blitz": minor
|
||||
"@blitzjs/auth": minor
|
||||
"@blitzjs/next": minor
|
||||
"@blitzjs/rpc": minor
|
||||
---
|
||||
|
||||
transpile packages to es2015 to support older browsers
|
||||
5
.changeset/calm-papayas-protect.md
Normal file
5
.changeset/calm-papayas-protect.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"blitz": minor
|
||||
---
|
||||
|
||||
When db.\$reset() rejects, reject with an Error object
|
||||
6
.changeset/curvy-days-attend.md
Normal file
6
.changeset/curvy-days-attend.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@blitzjs/rpc": patch
|
||||
"@blitzjs/generator": patch
|
||||
---
|
||||
|
||||
Switch from jest to vitest in new app templates
|
||||
8
.changeset/eighty-apes-sleep.md
Normal file
8
.changeset/eighty-apes-sleep.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"blitz": minor
|
||||
"@blitzjs/auth": minor
|
||||
"@blitzjs/next": minor
|
||||
"@blitzjs/rpc": minor
|
||||
---
|
||||
|
||||
Decoupled Blitz RPC from Blitz Auth to allow independent use.
|
||||
5
.changeset/few-hounds-worry.md
Normal file
5
.changeset/few-hounds-worry.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"blitz": minor
|
||||
---
|
||||
|
||||
some providers need extra attributes, update for wrapAppWithProvider
|
||||
5
.changeset/giant-students-carry.md
Normal file
5
.changeset/giant-students-carry.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/generator": patch
|
||||
---
|
||||
|
||||
Fix form paths when running blitz generate all
|
||||
7
.changeset/khaki-pens-rest.md
Normal file
7
.changeset/khaki-pens-rest.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@blitzjs/auth": minor
|
||||
"@blitzjs/next": minor
|
||||
"@blitzjs/rpc": minor
|
||||
---
|
||||
|
||||
fix Cannot read properties of null (reading 'isReady') for pnpm/yarn v3
|
||||
5
.changeset/long-hounds-melt.md
Normal file
5
.changeset/long-hounds-melt.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"blitz": major
|
||||
---
|
||||
|
||||
update paginate.ts, return more params for more complex pagination control
|
||||
5
.changeset/modern-games-dream.md
Normal file
5
.changeset/modern-games-dream.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"blitz": patch
|
||||
---
|
||||
|
||||
Export enhancePrisma for client again (Fixes #3964)
|
||||
5
.changeset/olive-dragons-drum.md
Normal file
5
.changeset/olive-dragons-drum.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/rpc": patch
|
||||
---
|
||||
|
||||
Add ability to put your query and mutation resolvers in a separate monorepo folder, allowing you to use them in multiple apps.
|
||||
@@ -44,7 +44,9 @@
|
||||
"@blitzjs/recipe-styled-components": "0.34.0-canary.0",
|
||||
"@blitzjs/recipe-tailwind": "0.34.0-canary.0",
|
||||
"@blitzjs/recipe-theme-ui": "0.34.0-canary.0",
|
||||
"@blitzjs/recipe-vanilla-extract": "0.34.0-canary.0"
|
||||
"@blitzjs/recipe-vanilla-extract": "0.34.0-canary.0",
|
||||
"test-rpc-path-root": "0.0.0",
|
||||
"test-full-auth-with-rpc": "0.0.0"
|
||||
},
|
||||
"changesets": [
|
||||
"afraid-dancers-juggle",
|
||||
@@ -52,14 +54,17 @@
|
||||
"big-phones-bow",
|
||||
"blue-flowers-peel",
|
||||
"blue-pigs-tan",
|
||||
"brave-zebras-deny",
|
||||
"breezy-bees-beg",
|
||||
"breezy-cameras-double",
|
||||
"breezy-moose-behave",
|
||||
"bright-mangos-run",
|
||||
"brown-avocados-wink",
|
||||
"calm-books-push",
|
||||
"calm-carpets-deny",
|
||||
"calm-horses-tie",
|
||||
"calm-nails-wait",
|
||||
"calm-papayas-protect",
|
||||
"calm-tomatoes-drive",
|
||||
"chilled-carrots-own",
|
||||
"chilly-nails-nail",
|
||||
@@ -71,11 +76,13 @@
|
||||
"cuddly-pugs-crash",
|
||||
"curly-rules-speak",
|
||||
"curly-seas-serve",
|
||||
"curvy-days-attend",
|
||||
"cyan-bulldogs-heal",
|
||||
"cyan-cars-greet",
|
||||
"dirty-monkeys-greet",
|
||||
"dirty-planets-chew",
|
||||
"early-lamps-itch",
|
||||
"eighty-apes-sleep",
|
||||
"eleven-humans-sort",
|
||||
"eleven-lobsters-drop",
|
||||
"empty-berries-rule",
|
||||
@@ -89,6 +96,7 @@
|
||||
"fast-papayas-grow",
|
||||
"fast-trainers-kneel",
|
||||
"few-dogs-fetch",
|
||||
"few-hounds-worry",
|
||||
"few-shrimps-leave",
|
||||
"flat-bees-approve",
|
||||
"fluffy-mangos-begin",
|
||||
@@ -104,6 +112,7 @@
|
||||
"gentle-dogs-reply",
|
||||
"gentle-lions-explode",
|
||||
"giant-mails-tap",
|
||||
"giant-students-carry",
|
||||
"gold-horses-punch",
|
||||
"good-apes-drum",
|
||||
"good-insects-wink",
|
||||
@@ -134,6 +143,7 @@
|
||||
"itchy-houses-marry",
|
||||
"itchy-spoons-tan",
|
||||
"khaki-ducks-cheer",
|
||||
"khaki-pens-rest",
|
||||
"kind-walls-suffer",
|
||||
"late-steaks-give",
|
||||
"lazy-maps-sort",
|
||||
@@ -145,6 +155,7 @@
|
||||
"little-pears-ring",
|
||||
"long-bees-hope",
|
||||
"long-dancers-jog",
|
||||
"long-hounds-melt",
|
||||
"long-lobsters-drop",
|
||||
"lovely-berries-sell",
|
||||
"lovely-colts-share",
|
||||
@@ -153,6 +164,7 @@
|
||||
"lucky-years-turn",
|
||||
"mean-gorillas-reply",
|
||||
"modern-cameras-pull",
|
||||
"modern-games-dream",
|
||||
"modern-ligers-behave",
|
||||
"moody-bags-walk",
|
||||
"moody-spoons-rhyme",
|
||||
@@ -171,6 +183,7 @@
|
||||
"ninety-rice-tickle",
|
||||
"odd-bears-run",
|
||||
"olive-bees-buy",
|
||||
"olive-dragons-drum",
|
||||
"olive-feet-rhyme",
|
||||
"olive-kings-invent",
|
||||
"olive-sheep-rhyme",
|
||||
@@ -189,6 +202,7 @@
|
||||
"purple-donkeys-smash",
|
||||
"purple-singers-greet",
|
||||
"quick-cycles-confess",
|
||||
"quick-dots-fetch",
|
||||
"quiet-feet-travel",
|
||||
"quiet-pans-hunt",
|
||||
"quiet-sloths-rule",
|
||||
@@ -196,14 +210,17 @@
|
||||
"red-badgers-retire",
|
||||
"red-gorillas-marry",
|
||||
"rich-chairs-invent",
|
||||
"rich-gorillas-develop",
|
||||
"rich-queens-travel",
|
||||
"rotten-rocks-remember",
|
||||
"rude-trainers-visit",
|
||||
"serious-mugs-leave",
|
||||
"shaggy-carpets-brake",
|
||||
"sharp-falcons-begin",
|
||||
"sharp-olives-sip",
|
||||
"shy-olives-hang",
|
||||
"shy-pumpkins-try",
|
||||
"shy-socks-eat",
|
||||
"silent-colts-reply",
|
||||
"silent-lies-run",
|
||||
"silly-apricots-share",
|
||||
@@ -241,12 +258,14 @@
|
||||
"tasty-news-collect",
|
||||
"ten-hairs-listen",
|
||||
"ten-rivers-burn",
|
||||
"tender-cooks-tie",
|
||||
"tender-pianos-check",
|
||||
"thick-parrots-float",
|
||||
"thirty-countries-build",
|
||||
"thirty-spies-applaud",
|
||||
"three-lies-pull",
|
||||
"tidy-clouds-smoke",
|
||||
"tidy-llamas-compare",
|
||||
"tough-toes-pull",
|
||||
"twelve-hornets-sip",
|
||||
"twelve-lemons-smile",
|
||||
|
||||
5
.changeset/quick-dots-fetch.md
Normal file
5
.changeset/quick-dots-fetch.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/rpc": minor
|
||||
---
|
||||
|
||||
Fix mutability bug in RPC configuration
|
||||
5
.changeset/rich-gorillas-develop.md
Normal file
5
.changeset/rich-gorillas-develop.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/rpc": patch
|
||||
---
|
||||
|
||||
fix: allow `GET` requests without `params` and `meta` keys
|
||||
5
.changeset/rude-trainers-visit.md
Normal file
5
.changeset/rude-trainers-visit.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"blitz": patch
|
||||
---
|
||||
|
||||
Fix blitz install for recipes that use the path helper to check if ./src/pages directory is available, otherwise use ./pages
|
||||
5
.changeset/shy-socks-eat.md
Normal file
5
.changeset/shy-socks-eat.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"blitz": patch
|
||||
---
|
||||
|
||||
using "blitz install" inside recipe with addRunCommandStep causes hangs up
|
||||
7
.changeset/tender-cooks-tie.md
Normal file
7
.changeset/tender-cooks-tie.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"blitz": patch
|
||||
"@blitzjs/next": patch
|
||||
"@blitzjs/rpc": patch
|
||||
---
|
||||
|
||||
Fix ambigious class warning log & upgrade superjson from 1.9.1 to 1.11.0
|
||||
5
.changeset/tidy-llamas-compare.md
Normal file
5
.changeset/tidy-llamas-compare.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/rpc": minor
|
||||
---
|
||||
|
||||
fix resolverPath:root and make it work with monorepo resolvers
|
||||
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -1,5 +1,5 @@
|
||||
# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners
|
||||
|
||||
* @beerose @dillonraphael
|
||||
* @dillonraphael
|
||||
|
||||
packages/generator/templates**/* @flybayer
|
||||
|
||||
59
.github/workflows/main.yml
vendored
59
.github/workflows/main.yml
vendored
@@ -8,10 +8,6 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
TURBO_TOKEN: 05de0230f01174d1f8cb4845a01dc6c895ce28f04ebef2318ab11615791b871c35eabbf8
|
||||
TURBO_TEAM: foo
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
@@ -20,7 +16,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2
|
||||
with:
|
||||
version: 6.32.6
|
||||
version: 7.11.0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
@@ -29,15 +25,10 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- run: pnpm manypkg check
|
||||
- name: Turborepo local server
|
||||
uses: felixmosh/turborepo-gh-artifacts@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
server-token: ${{ env.TURBO_TOKEN }}
|
||||
- name: Build
|
||||
run: pnpm build -- --api="http://127.0.0.1:9080"
|
||||
run: pnpm build
|
||||
- name: Lint
|
||||
run: pnpm lint -- --api="http://127.0.0.1:9080"
|
||||
run: pnpm lint
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -46,23 +37,17 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2
|
||||
with:
|
||||
version: 6.32.6
|
||||
version: 7.11.0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16
|
||||
cache: "pnpm"
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm manypkg check
|
||||
- name: Turborepo local server
|
||||
uses: felixmosh/turborepo-gh-artifacts@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
server-token: ${{ env.TURBO_TOKEN }}
|
||||
- name: Build
|
||||
run: pnpm build -- --api="http://127.0.0.1:9080"
|
||||
run: pnpm build
|
||||
- name: Build Apps
|
||||
run: pnpm build:apps -- --api="http://127.0.0.1:9080"
|
||||
run: pnpm build:apps
|
||||
|
||||
Unit-Tests:
|
||||
name: "Unit Test: ${{ matrix.os }} (node@16)"
|
||||
@@ -82,7 +67,7 @@ jobs:
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2
|
||||
with:
|
||||
version: 6.32.6
|
||||
version: 7.11.0
|
||||
|
||||
- name: Setup node@16
|
||||
uses: actions/setup-node@v2
|
||||
@@ -94,17 +79,8 @@ jobs:
|
||||
run: pnpm install --frozen-lockfile
|
||||
shell: bash
|
||||
|
||||
- run: pnpm manypkg check
|
||||
shell: bash
|
||||
|
||||
- name: Turborepo local server
|
||||
uses: felixmosh/turborepo-gh-artifacts@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
server-token: ${{ env.TURBO_TOKEN }}
|
||||
|
||||
- name: Build
|
||||
run: pnpm build -- --api="http://127.0.0.1:9080"
|
||||
run: pnpm build
|
||||
shell: bash
|
||||
|
||||
- name: Test Packages
|
||||
@@ -150,7 +126,7 @@ jobs:
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2
|
||||
with:
|
||||
version: 6.32.6
|
||||
version: 7.11.0
|
||||
|
||||
- name: Setup node@${{ matrix.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
@@ -158,25 +134,18 @@ jobs:
|
||||
node-version: ${{ matrix.NODE_VERSION }}
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Link Blitz CLI
|
||||
run: pnpm link ./packages/blitz
|
||||
shell: bash
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
shell: bash
|
||||
|
||||
- run: pnpm manypkg check
|
||||
- name: Install playwright
|
||||
run: |
|
||||
npm i -g playwright
|
||||
PLAYWRIGHT_BROWSERS_PATH=$HOME/pw-browsers npx playwright install
|
||||
shell: bash
|
||||
|
||||
- name: Turborepo local server
|
||||
uses: felixmosh/turborepo-gh-artifacts@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
server-token: ${{ env.TURBO_TOKEN }}
|
||||
|
||||
- name: Build
|
||||
run: pnpm build -- --api="http://127.0.0.1:9080"
|
||||
run: pnpm build
|
||||
shell: bash
|
||||
|
||||
- name: Test Packages
|
||||
|
||||
11
README.md
11
README.md
@@ -6,7 +6,7 @@
|
||||
<img alt="" src="https://img.shields.io/badge/Join%20our%20community-6700EB.svg?style=for-the-badge&labelColor=000000&logoWidth=20&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAQ9SURBVHgB7d3dVdtAEIbhcSpICUoH0IEogQqSVBBSAU4FSSpIOoAORAfQgSghHXzZ1U/YcMD4R9rZmf2ec3y448LyiNf27iLiGIAmPLrweC9Un3DhrzG6EarLNP09nlwJ1SOZ/lQr5N80/S/p2QMVCBf5N17XCfm1Y/rBHqjAG9PPHvBsz+mf9WAP+HLA9M/YA14cOP2payH7jpj+VCtk1wnTP+vj7xCy6cTpn7EHLMLp059iD1iD8eveJbVCNsSLheX1YA/YgOWnf8YeKB3Wmf7Ud6Fy4f/FHmtpxbl3YlC4MJ/Cj0bWdwPnPbARg+L0S54XQHS32WwuxClzd4CM0z9rPfeAuTtA5ulPXYQ7wZ04Y+oOoDD9KZc9YOoOoDj9s4dwFzgXR6w1wIPoOvPWA9buAHEJ173o3gWiy3AnuBUHLEbgmYwvAk1/wuM8vAgexThzbwPDkx7/DHwVXfFOxP2GmsKd4Ab6zPeAyU8CI7AHFmH2BRCBPXAyk18GzUrqAXCTiR4ssyj0VFw/oCU8+e+RZ33AWz6KMaYbIIWxB+JSLs1bsbkeMN0AqakHvoku9oA2sAfqBvbAQdw0QArsgb25aYBUQT3QgT2gB+yBuqGcHij2UCqXDZACe2Anlw2QYg/QAOyBuoE98CL3DZDCuK4/rh/Q7oGL6U+TOvcNkJoijN8X1C48+T+g75eQDrAH/qmqAVJgDwyqaoAUe4AGYA/UDZX3QLUNkEIZPRCd5+6BahsgVUgPROwBTSijB7jpVAvGHriHvmw9wAZ4BpX1ABvgmakHtPcbRuwBTWAPULgAV9D/jKDY9YRvwvgEaurD44uQHvAol7qBW7WKluVtIHiUS7GyvA0s6CiXDnxrpQfsgbqBS7GKk/2jYHCrVlGyfxTMrVo0ALdq1Q3sgSKofh0M9oA61a+D2QM0AHugbmAPqClmSRjK2apVVQ8UsySsoK1aHdgDesCtWnUDeyCrIpeFg1u3sylyWTi3btMA7IG6gT2wuuK3hoE9sKrit4YVslWLPaAN7IG6ocKt2zmY2h4O9sDiTG0PZw/QANy6XTewBxZj9ogYVHy025LMHhEz9cBn0We6B0yfERReBLfhx0/R1YQHPx/QBPbA0VwcEwf2wNFcHBPHHjiem3MC2QPHcXdSaJjA+KfgTPQ8hhfjBzHC40mhlzJ+Xq9lK4a4PCs43AVaGTed5mZq+iOXZwWHi3AnOj2wFWNcnxYe7gTxLtBKHuamP/J+Wnh8a5irB7ZC5Yk9gPX1QuXC+usHWqGyhYvUYR0a7zboUOFCNVhnk0krZAOW7wFOvzXhom2xnEbIHizTA1wEYhWW6YFGyC6c1gOcfg9wfA80Qj7g8B7g9HuCww+haIR8wf49wOn3Cvv9k8tGyC/s7gFOv3fY3QONkH+v9MBWqB7PeqDn9FcIT//kcitUn6kHOu/T/xfWzlQy3dEHhwAAAABJRU5ErkJggg==">
|
||||
</a>
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-396-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
|
||||
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-399-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
<a aria-label="License" href="https://github.com/blitz-js/blitz/blob/main/LICENSE">
|
||||
<img alt="" src="https://img.shields.io/npm/l/blitz.svg?style=for-the-badge&labelColor=000000&color=blue">
|
||||
@@ -136,8 +136,6 @@ Your financial contributions help ensure Blitz continues to be developed and mai
|
||||
<tr>
|
||||
<td align="center"><a href="https://twitter.com/flybayer"><img src="https://avatars3.githubusercontent.com/u/8813276?v=4" width="100px;" alt=""/><br /><sub><b>Brandon Bayer</b></sub></a><br />Creator</td>
|
||||
<td align="center"><a href="http://twitter.com/dillonraphael"><img src="https://avatars.githubusercontent.com/u/3496193?v=4" width="100px;" alt=""/><br /><sub><b>Dillon Raphael</b></sub></a><br />Lead Maintainer</td>
|
||||
<td align="center"><a href="http://aleksandra.codes"><img src="https://avatars.githubusercontent.com/u/9019397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aleksandra Sikora</b></sub></a><br />Core Maintainer</td>
|
||||
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
@@ -535,7 +533,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://bhanuteja.dev/"><img src="https://avatars.githubusercontent.com/u/17903466?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bhanu Teja Pachipulusu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=pbteja1998" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://twitter.com/pavestru"><img src="https://avatars.githubusercontent.com/u/10186479?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pavel Struhar</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=pavestru" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://in-thepink.com/"><img src="https://avatars.githubusercontent.com/u/42126368?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Reo Ishiyama</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=reo777" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://macwright.com/"><img src="https://avatars.githubusercontent.com/u/32314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tom MacWright</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tmcw" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://macwright.com/"><img src="https://avatars.githubusercontent.com/u/32314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tom MacWright</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tmcw" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=tmcw" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=tmcw" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://francoisbest.com"><img src="https://avatars.githubusercontent.com/u/1174092?v=4?s=100" width="100px;" alt=""/><br /><sub><b>François Best</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=franky47" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -617,7 +615,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://github.com/ricardo-rp"><img src="https://avatars.githubusercontent.com/u/30808767?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ricardo Romero</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ricardo-rp" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="exocortex.anothernode.com"><img src="https://avatars.githubusercontent.com/u/3286144?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Moritz Reiter</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=anothernode" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://msich.dev"><img src="https://avatars.githubusercontent.com/u/38794918?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt Sichterman</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=msichterman" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/medihack"><img src="https://avatars.githubusercontent.com/u/120626?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kai Schlamp</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=medihack" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/medihack"><img src="https://avatars.githubusercontent.com/u/120626?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kai Schlamp</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=medihack" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=medihack" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://muyiwa.me"><img src="https://avatars.githubusercontent.com/u/6832244?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muyiwa Olu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=muyiwaolu" title="Code">💻</a></td>
|
||||
@@ -732,6 +730,9 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://github.com/oloost"><img src="https://avatars.githubusercontent.com/u/72395941?v=4?s=100" width="100px;" alt=""/><br /><sub><b>oloost</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=oloost" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/janvennemann"><img src="https://avatars.githubusercontent.com/u/1406024?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jan Vennemann</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=janvennemann" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=janvennemann" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://kevinjones.engineer"><img src="https://avatars.githubusercontent.com/u/20748598?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Jones</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=joneskj55" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/paulm17"><img src="https://avatars.githubusercontent.com/u/387463?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Paul</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=paulm17" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=paulm17" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=paulm17" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/selcukfatihsevinc"><img src="https://avatars.githubusercontent.com/u/384836?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Selçuk Fatih Sevinç</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=selcukfatihsevinc" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=selcukfatihsevinc" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/usamaster"><img src="https://avatars.githubusercontent.com/u/5255330?v=4?s=100" width="100px;" alt=""/><br /><sub><b>usamaster</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=usamaster" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"@hookform/error-message": "2.0.0",
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "4.6.0",
|
||||
"blitz": "workspace:2.0.0-beta.17",
|
||||
"blitz": "workspace:2.0.0-beta.20",
|
||||
"next": "12.2.5",
|
||||
"openid-client": "5.2.1",
|
||||
"prisma": "4.6.0",
|
||||
|
||||
@@ -4,7 +4,7 @@ TODO
|
||||
|
||||
This is a [Blitz.js](https://github.com/blitz-js/blitz) app.
|
||||
|
||||
# ****name****
|
||||
# \***\*name\*\***
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -38,7 +38,7 @@ Runs your tests using Jest.
|
||||
yarn test
|
||||
```
|
||||
|
||||
Blitz comes with a test setup using [Jest](https://jestjs.io/) and [react-testing-library](https://testing-library.com/).
|
||||
Blitz comes with a test setup using [Vitest](https://vitest.dev/) and [react-testing-library](https://testing-library.com/).
|
||||
|
||||
## Commands
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
const nextJest = require("@blitzjs/next/jest")
|
||||
|
||||
const createJestConfig = nextJest({
|
||||
dir: "./",
|
||||
})
|
||||
|
||||
module.exports = createJestConfig(customJestConfig)
|
||||
@@ -8,7 +8,8 @@
|
||||
"lint": "next lint",
|
||||
"prisma:start": "prisma generate && prisma migrate deploy",
|
||||
"prisma:studio": "prisma studio",
|
||||
"test:local": "jest"
|
||||
"test:local": "prisma generate && vitest run --passWithNoTests",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"prisma": {
|
||||
"schema": "db/schema.prisma"
|
||||
@@ -30,7 +31,7 @@
|
||||
"@hookform/error-message": "2.0.0",
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "4.6.0",
|
||||
"blitz": "workspace:2.0.0-beta.17",
|
||||
"blitz": "workspace:2.0.0-beta.20",
|
||||
"next": "12.2.5",
|
||||
"prisma": "4.6.0",
|
||||
"react": "18.2.0",
|
||||
@@ -41,25 +42,27 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@types/jest": "29.2.2",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/preview-email": "2.0.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@typescript-eslint/eslint-plugin": "5.42.1",
|
||||
"@vitejs/plugin-react": "2.2.0",
|
||||
"eslint": "8.27.0",
|
||||
"eslint-config-next": "12.3.1",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"husky": "8.0.2",
|
||||
"jest": "29.3.0",
|
||||
"jest-environment-jsdom": "29.3.0",
|
||||
"jsdom": "20.0.3",
|
||||
"lint-staged": "13.0.3",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-prisma": "4.4.0",
|
||||
"pretty-quick": "3.1.3",
|
||||
"preview-email": "3.0.7",
|
||||
"typescript": "^4.8.4"
|
||||
"typescript": "^4.8.4",
|
||||
"vite-tsconfig-paths": "3.6.0",
|
||||
"vitest": "0.25.3"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
||||
64
apps/toolkit-app/src/auth/mutations/forgotPassword.test.ts
Normal file
64
apps/toolkit-app/src/auth/mutations/forgotPassword.test.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { vi, describe, it, beforeEach } from "vitest"
|
||||
import db from "db"
|
||||
import { hash256 } from "@blitzjs/auth"
|
||||
import forgotPassword from "./forgotPassword"
|
||||
import previewEmail from "preview-email"
|
||||
import { Ctx } from "@blitzjs/next"
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.$reset()
|
||||
})
|
||||
|
||||
const generatedToken = "plain-token"
|
||||
vi.mock("@blitzjs/auth", async () => {
|
||||
const auth = await vi.importActual<Record<string, unknown>>("@blitzjs/auth")!
|
||||
return {
|
||||
...auth,
|
||||
generateToken: () => generatedToken,
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock("preview-email", () => ({ default: vi.fn() }))
|
||||
|
||||
describe("forgotPassword mutation", () => {
|
||||
it("does not throw error if user doesn't exist", async () => {
|
||||
await expect(forgotPassword({ email: "no-user@email.com" }, {} as Ctx)).resolves.not.toThrow()
|
||||
})
|
||||
|
||||
it("works correctly", async () => {
|
||||
// Create test user
|
||||
const user = await db.user.create({
|
||||
data: {
|
||||
email: "user@example.com",
|
||||
tokens: {
|
||||
// Create old token to ensure it's deleted
|
||||
create: {
|
||||
type: "RESET_PASSWORD",
|
||||
hashedToken: "token",
|
||||
expiresAt: new Date(),
|
||||
sentTo: "user@example.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
include: { tokens: true },
|
||||
})
|
||||
|
||||
// Invoke the mutation
|
||||
await forgotPassword({ email: user.email }, {} as Ctx)
|
||||
|
||||
const tokens = await db.token.findMany({ where: { userId: user.id } })
|
||||
const token = tokens[0]
|
||||
if (!user.tokens[0]) throw new Error("Missing user token")
|
||||
if (!token) throw new Error("Missing token")
|
||||
|
||||
// delete's existing tokens
|
||||
expect(tokens.length).toBe(1)
|
||||
|
||||
expect(token.id).not.toBe(user.tokens[0].id)
|
||||
expect(token.type).toBe("RESET_PASSWORD")
|
||||
expect(token.sentTo).toBe(user.email)
|
||||
expect(token.hashedToken).toBe(hash256(generatedToken))
|
||||
expect(token.expiresAt > new Date()).toBe(true)
|
||||
expect(previewEmail).toBeCalled()
|
||||
})
|
||||
})
|
||||
83
apps/toolkit-app/src/auth/mutations/resetPassword.test.ts
Normal file
83
apps/toolkit-app/src/auth/mutations/resetPassword.test.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { vi, describe, it, beforeEach, expect } from "vitest"
|
||||
import resetPassword from "./resetPassword"
|
||||
import db from "db"
|
||||
import { SecurePassword, hash256 } from "@blitzjs/auth"
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.$reset()
|
||||
})
|
||||
|
||||
const mockCtx: any = {
|
||||
session: {
|
||||
$create: vi.fn(),
|
||||
},
|
||||
}
|
||||
|
||||
describe("resetPassword mutation", () => {
|
||||
it("works correctly", async () => {
|
||||
expect(true).toBe(true)
|
||||
|
||||
// Create test user
|
||||
const goodToken = "randomPasswordResetToken"
|
||||
const expiredToken = "expiredRandomPasswordResetToken"
|
||||
const future = new Date()
|
||||
future.setHours(future.getHours() + 4)
|
||||
const past = new Date()
|
||||
past.setHours(past.getHours() - 4)
|
||||
|
||||
const user = await db.user.create({
|
||||
data: {
|
||||
email: "user@example.com",
|
||||
tokens: {
|
||||
// Create old token to ensure it's deleted
|
||||
create: [
|
||||
{
|
||||
type: "RESET_PASSWORD",
|
||||
hashedToken: hash256(expiredToken),
|
||||
expiresAt: past,
|
||||
sentTo: "user@example.com",
|
||||
},
|
||||
{
|
||||
type: "RESET_PASSWORD",
|
||||
hashedToken: hash256(goodToken),
|
||||
expiresAt: future,
|
||||
sentTo: "user@example.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
include: { tokens: true },
|
||||
})
|
||||
|
||||
const newPassword = "newPassword"
|
||||
|
||||
// Non-existent token
|
||||
await expect(
|
||||
resetPassword({ token: "no-token", password: "", passwordConfirmation: "" }, mockCtx)
|
||||
).rejects.toThrowError()
|
||||
|
||||
// Expired token
|
||||
await expect(
|
||||
resetPassword(
|
||||
{ token: expiredToken, password: newPassword, passwordConfirmation: newPassword },
|
||||
mockCtx
|
||||
)
|
||||
).rejects.toThrowError()
|
||||
|
||||
// Good token
|
||||
await resetPassword(
|
||||
{ token: goodToken, password: newPassword, passwordConfirmation: newPassword },
|
||||
mockCtx
|
||||
)
|
||||
|
||||
// Delete's the token
|
||||
const numberOfTokens = await db.token.count({ where: { userId: user.id } })
|
||||
expect(numberOfTokens).toBe(0)
|
||||
|
||||
// Updates user's password
|
||||
const updatedUser = await db.user.findFirst({ where: { id: user.id } })
|
||||
expect(await SecurePassword.verify(updatedUser!.hashedPassword, newPassword)).toBe(
|
||||
SecurePassword.VALID
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AuthClientPlugin } from "@blitzjs/auth"
|
||||
import { setupBlitzClient } from "@blitzjs/next"
|
||||
import { BlitzRpcPlugin } from "@blitzjs/rpc"
|
||||
import { BlitzCustomPlugin } from "./custom-plugin/plugin"
|
||||
|
||||
export const { withBlitz } = setupBlitzClient({
|
||||
plugins: [
|
||||
@@ -8,5 +9,6 @@ export const { withBlitz } = setupBlitzClient({
|
||||
cookiePrefix: "web-cookie-prefix",
|
||||
}),
|
||||
BlitzRpcPlugin({}),
|
||||
BlitzCustomPlugin({}),
|
||||
],
|
||||
})
|
||||
|
||||
38
apps/toolkit-app/src/custom-plugin/plugin.ts
Normal file
38
apps/toolkit-app/src/custom-plugin/plugin.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { createClientPlugin } from "blitz"
|
||||
|
||||
type CustomPluginOptions = {
|
||||
// ... your options
|
||||
}
|
||||
|
||||
export const BlitzCustomPlugin = createClientPlugin<CustomPluginOptions, {}>(
|
||||
(options?: CustomPluginOptions) => {
|
||||
// ... your plugin code
|
||||
console.log("Custom plugin loaded")
|
||||
return {
|
||||
events: {
|
||||
onSessionCreated: async () => {
|
||||
// Called when a new session is created - Usually when the user logs in or logs out
|
||||
console.log("onSessionCreated in custom plugin")
|
||||
},
|
||||
onRpcError: async () => {
|
||||
// Called when an RPC call fails
|
||||
console.log("onRpcError in custom plugin")
|
||||
},
|
||||
},
|
||||
middleware: {
|
||||
beforeHttpRequest: (req) => {
|
||||
//make changes to the request options before RPC call
|
||||
req.headers = { ...req.headers, ...{ customHeader: "customHeaderValue" } }
|
||||
return req
|
||||
},
|
||||
beforeHttpResponse: (res) => {
|
||||
//make changes to the response before returning to the caller
|
||||
return res
|
||||
},
|
||||
},
|
||||
exports: () => ({
|
||||
// ... your exports
|
||||
}),
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -7,7 +7,6 @@ import logout from "src/auth/mutations/logout"
|
||||
import logo from "public/logo.png"
|
||||
import { useMutation } from "@blitzjs/rpc"
|
||||
import { Routes, BlitzPage } from "@blitzjs/next"
|
||||
import { getSession, useSession } from "@blitzjs/auth"
|
||||
|
||||
/*
|
||||
* This file is just for a pleasant getting started page for your new app.
|
||||
|
||||
@@ -1,27 +1,32 @@
|
||||
import { useCurrentUser } from "src/users/hooks/useCurrentUser"
|
||||
/**
|
||||
* @vitest-environment jsdom
|
||||
*/
|
||||
|
||||
import { expect, vi, test } from "vitest"
|
||||
import { render } from "test/utils"
|
||||
|
||||
import Home from "../src/pages/index"
|
||||
|
||||
jest.mock("src/users/hooks/useCurrentUser")
|
||||
const mockUseCurrentUser = useCurrentUser as jest.MockedFunction<typeof useCurrentUser>
|
||||
vi.mock("public/logo.png", () => ({
|
||||
default: { src: "/logo.png" },
|
||||
}))
|
||||
|
||||
describe("renders blitz documentation link", () => {
|
||||
it("test", () => {
|
||||
// This is an example of how to ensure a specific item is in the document
|
||||
// But it's disabled by default (by test.skip) so the test doesn't fail
|
||||
// when you remove the the default content from the page
|
||||
test.skip("renders blitz documentation link", () => {
|
||||
// This is an example of how to ensure a specific item is in the document
|
||||
// But it's disabled by default (by test.skip) so the test doesn't fail
|
||||
// when you remove the the default content from the page
|
||||
|
||||
// This is an example on how to mock api hooks when testing
|
||||
mockUseCurrentUser.mockReturnValue({
|
||||
// This is an example on how to mock api hooks when testing
|
||||
vi.mock("src/users/hooks/useCurrentUser", () => ({
|
||||
useCurrentUser: () => ({
|
||||
id: 1,
|
||||
name: "User",
|
||||
email: "user@email.com",
|
||||
role: "user",
|
||||
})
|
||||
}),
|
||||
}))
|
||||
|
||||
const { getByText } = render(<Home />)
|
||||
const linkElement = getByText(/Documentation/i)
|
||||
expect(linkElement).toBeInTheDocument()
|
||||
})
|
||||
const { getByText } = render(<Home />)
|
||||
const linkElement = getByText(/Documentation/i)
|
||||
expect(linkElement).toBeInTheDocument()
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// This is the jest 'setupFilesAfterEnv' setup file
|
||||
// It's a good place to set globals, add global before/after hooks, etc
|
||||
import "@testing-library/jest-dom"
|
||||
|
||||
export {} // so TS doesn't complain
|
||||
export {}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { vi } from "vitest"
|
||||
import { render as defaultRender } from "@testing-library/react"
|
||||
import { renderHook as defaultRenderHook } from "@testing-library/react-hooks"
|
||||
import { NextRouter } from "next/router"
|
||||
@@ -82,16 +83,16 @@ export const mockRouter: NextRouter = {
|
||||
isReady: true,
|
||||
isLocaleDomain: false,
|
||||
isPreview: false,
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
back: jest.fn(),
|
||||
prefetch: jest.fn(),
|
||||
beforePopState: jest.fn(),
|
||||
push: vi.fn(),
|
||||
replace: vi.fn(),
|
||||
reload: vi.fn(),
|
||||
back: vi.fn(),
|
||||
prefetch: vi.fn(),
|
||||
beforePopState: vi.fn(),
|
||||
events: {
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
emit: jest.fn(),
|
||||
on: vi.fn(),
|
||||
off: vi.fn(),
|
||||
emit: vi.fn(),
|
||||
},
|
||||
isFallback: false,
|
||||
}
|
||||
|
||||
17
apps/toolkit-app/vitest.config.ts
Normal file
17
apps/toolkit-app/vitest.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineConfig } from "vitest/config"
|
||||
|
||||
import react from "@vitejs/plugin-react"
|
||||
import tsconfigPaths from "vite-tsconfig-paths"
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tsconfigPaths()],
|
||||
test: {
|
||||
dir: "./",
|
||||
globals: true,
|
||||
setupFiles: "./test/setup.ts",
|
||||
coverage: {
|
||||
reporter: ["text", "json", "html"],
|
||||
},
|
||||
},
|
||||
})
|
||||
2
integration-tests/auth-with-rpc/.env
Normal file
2
integration-tests/auth-with-rpc/.env
Normal file
@@ -0,0 +1,2 @@
|
||||
SESSION_SECRET_KEY=hsdenhJfpLHrGjgdgg3jdF8g2bYD2PaQ
|
||||
HEADLESS=true
|
||||
1
integration-tests/auth-with-rpc/.eslintrc.js
Normal file
1
integration-tests/auth-with-rpc/.eslintrc.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require("@blitzjs/next/eslint")
|
||||
3
integration-tests/auth-with-rpc/.gitignore
vendored
Normal file
3
integration-tests/auth-with-rpc/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
# Keep environment variables out of version control
|
||||
*.sqlite
|
||||
7
integration-tests/auth-with-rpc/db/index.ts
Normal file
7
integration-tests/auth-with-rpc/db/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import {enhancePrisma} from "blitz"
|
||||
import {PrismaClient} from "@prisma/client"
|
||||
|
||||
const EnhancedPrisma = enhancePrisma(PrismaClient)
|
||||
export * from "@prisma/client"
|
||||
const prisma = new EnhancedPrisma()
|
||||
export default prisma
|
||||
@@ -0,0 +1,47 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
"name" TEXT,
|
||||
"email" TEXT NOT NULL,
|
||||
"hashedPassword" TEXT,
|
||||
"role" TEXT NOT NULL DEFAULT 'user'
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Session" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
"expiresAt" DATETIME,
|
||||
"handle" TEXT NOT NULL,
|
||||
"userId" INTEGER,
|
||||
"hashedSessionToken" TEXT,
|
||||
"antiCSRFToken" TEXT,
|
||||
"publicData" TEXT,
|
||||
"privateData" TEXT,
|
||||
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Token" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
"hashedToken" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"expiresAt" DATETIME NOT NULL,
|
||||
"sentTo" TEXT NOT NULL,
|
||||
"userId" INTEGER NOT NULL,
|
||||
CONSTRAINT "Token_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Session_handle_key" ON "Session"("handle");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Token_hashedToken_type_key" ON "Token"("hashedToken", "type");
|
||||
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "sqlite"
|
||||
50
integration-tests/auth-with-rpc/db/schema.prisma
Normal file
50
integration-tests/auth-with-rpc/db/schema.prisma
Normal file
@@ -0,0 +1,50 @@
|
||||
datasource sqlite {
|
||||
provider = "sqlite"
|
||||
url = "file:./db.sqlite"
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
name String?
|
||||
email String @unique
|
||||
hashedPassword String?
|
||||
role String @default("user")
|
||||
|
||||
sessions Session[]
|
||||
tokens Token[]
|
||||
}
|
||||
|
||||
model Session {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
expiresAt DateTime?
|
||||
handle String @unique
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
userId Int?
|
||||
hashedSessionToken String?
|
||||
antiCSRFToken String?
|
||||
publicData String?
|
||||
privateData String?
|
||||
}
|
||||
|
||||
model Token {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
hashedToken String
|
||||
type String
|
||||
expiresAt DateTime
|
||||
sentTo String
|
||||
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId Int
|
||||
|
||||
@@unique([hashedToken, type])
|
||||
}
|
||||
18
integration-tests/auth-with-rpc/db/seed.ts
Normal file
18
integration-tests/auth-with-rpc/db/seed.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import prisma from "./index"
|
||||
import {SecurePassword} from "@blitzjs/auth"
|
||||
|
||||
const seed = async () => {
|
||||
const hashedPassword = await SecurePassword.hash("abcd1234")
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
email: "test@test.com",
|
||||
hashedPassword,
|
||||
role: "user",
|
||||
},
|
||||
})
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
seed()
|
||||
|
||||
export default seed
|
||||
5
integration-tests/auth-with-rpc/next-env.d.ts
vendored
Normal file
5
integration-tests/auth-with-rpc/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
2
integration-tests/auth-with-rpc/next.config.js
Normal file
2
integration-tests/auth-with-rpc/next.config.js
Normal file
@@ -0,0 +1,2 @@
|
||||
const {withBlitz} = require("@blitzjs/next")
|
||||
module.exports = withBlitz({})
|
||||
62
integration-tests/auth-with-rpc/package.json
Normal file
62
integration-tests/auth-with-rpc/package.json
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"name": "test-full-auth-with-rpc",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"prisma": {
|
||||
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} db/seed.ts",
|
||||
"schema": "db/schema.prisma"
|
||||
},
|
||||
"scripts": {
|
||||
"start:dev": "pnpm run prisma:start && blitz dev",
|
||||
"test": "vitest run",
|
||||
"test-watch": "vitest",
|
||||
"start": "blitz start",
|
||||
"lint": "next lint",
|
||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
|
||||
"prisma:start": "blitz prisma migrate deploy",
|
||||
"prisma:studio": "prisma studio"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/config": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/next": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/rpc": "workspace:2.0.0-beta.20",
|
||||
"@hookform/error-message": "2.0.0",
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "4.6.0",
|
||||
"blitz": "workspace:2.0.0-beta.20",
|
||||
"delay": "5.0.0",
|
||||
"next": "12.2.5",
|
||||
"prisma": "4.6.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "7.39.1",
|
||||
"ts-node": "10.9.1",
|
||||
"zod": "3.19.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/preview-email": "2.0.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@typescript-eslint/eslint-plugin": "5.42.1",
|
||||
"@vitejs/plugin-react": "2.2.0",
|
||||
"eslint": "8.27.0",
|
||||
"eslint-config-next": "12.3.1",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"husky": "8.0.2",
|
||||
"jsdom": "20.0.3",
|
||||
"lint-staged": "13.0.3",
|
||||
"playwright": "1.28.0",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-prisma": "4.4.0",
|
||||
"pretty-quick": "3.1.3",
|
||||
"preview-email": "3.0.7",
|
||||
"typescript": "^4.8.4",
|
||||
"vite-tsconfig-paths": "3.6.0",
|
||||
"vitest": "0.25.3"
|
||||
}
|
||||
}
|
||||
14
integration-tests/auth-with-rpc/src/blitz-client.ts
Normal file
14
integration-tests/auth-with-rpc/src/blitz-client.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import {AuthClientPlugin} from "@blitzjs/auth"
|
||||
import {setupBlitzClient} from "@blitzjs/next"
|
||||
import {BlitzRpcPlugin} from "@blitzjs/rpc"
|
||||
import {BlitzCustomPlugin} from "./custom-plugin/plugin"
|
||||
|
||||
export const {withBlitz} = setupBlitzClient({
|
||||
plugins: [
|
||||
AuthClientPlugin({
|
||||
cookiePrefix: "web-cookie-prefix",
|
||||
}),
|
||||
BlitzRpcPlugin({}),
|
||||
BlitzCustomPlugin({}),
|
||||
],
|
||||
})
|
||||
23
integration-tests/auth-with-rpc/src/blitz-server.ts
Normal file
23
integration-tests/auth-with-rpc/src/blitz-server.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type {BlitzCliConfig} from "blitz"
|
||||
import {setupBlitzServer} from "@blitzjs/next"
|
||||
import {AuthServerPlugin, PrismaStorage} from "@blitzjs/auth"
|
||||
import db from "../db"
|
||||
import {simpleRolesIsAuthorized} from "@blitzjs/auth"
|
||||
import {BlitzLogger} from "blitz"
|
||||
|
||||
const {gSSP, gSP, api} = setupBlitzServer({
|
||||
plugins: [
|
||||
AuthServerPlugin({
|
||||
cookiePrefix: "web-cookie-prefix",
|
||||
storage: PrismaStorage(db),
|
||||
isAuthorized: simpleRolesIsAuthorized,
|
||||
}),
|
||||
],
|
||||
logger: BlitzLogger({}),
|
||||
})
|
||||
|
||||
export {gSSP, gSP, api}
|
||||
|
||||
export const cliConfig: BlitzCliConfig = {
|
||||
customTemplates: "app/templates",
|
||||
}
|
||||
50
integration-tests/auth-with-rpc/src/custom-plugin/plugin.ts
Normal file
50
integration-tests/auth-with-rpc/src/custom-plugin/plugin.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import {createClientPlugin} from "blitz"
|
||||
|
||||
type CustomPluginOptions = {
|
||||
// ... your options
|
||||
}
|
||||
|
||||
export const BlitzCustomPlugin = createClientPlugin<CustomPluginOptions, {}>(
|
||||
(options?: CustomPluginOptions) => {
|
||||
// ... your plugin code
|
||||
console.info("Custom plugin loaded")
|
||||
return {
|
||||
events: {
|
||||
onSessionCreated: async () => {
|
||||
// ... Called when a new session is created - Usually when the user logs in or logs out
|
||||
// if the document url is /custom-plugin then write message to the document
|
||||
if (document.location.pathname === "/custom-plugin") {
|
||||
//find the content in div id page and write message
|
||||
document.getElementById("page")!.innerText = "Custom plugin Session Created"
|
||||
}
|
||||
},
|
||||
onRpcError: async () => {
|
||||
// ... Called when an RPC call fails
|
||||
if (document.location.pathname === "/custom-plugin") {
|
||||
document.getElementById("page")!.innerText = "Custom plugin RPC Error"
|
||||
}
|
||||
},
|
||||
},
|
||||
middleware: {
|
||||
beforeHttpRequest: (req) => {
|
||||
// ... make changes to the request options before RPC call
|
||||
if (document.location.pathname === "/custom-plugin") {
|
||||
req.headers = {...req.headers, ...{customHeader: "customHeaderValue"}}
|
||||
document.getElementById("before-req")!.innerText = req.headers["customHeader"]
|
||||
}
|
||||
return req
|
||||
},
|
||||
beforeHttpResponse: (res) => {
|
||||
// ... make changes to the response before returning to the caller
|
||||
if (document.location.pathname === "/custom-plugin") {
|
||||
document.getElementById("before-res")!.innerText = res.headers.get("content-length")!
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
exports: () => ({
|
||||
// ... your exports
|
||||
}),
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
import {BlitzLayout} from "@blitzjs/next"
|
||||
|
||||
const AuthenticateLayout: BlitzLayout = ({children}) => {
|
||||
return <div id="layout">{children}</div>
|
||||
}
|
||||
|
||||
AuthenticateLayout.authenticate = true
|
||||
|
||||
export default AuthenticateLayout
|
||||
@@ -0,0 +1,9 @@
|
||||
import {BlitzLayout} from "@blitzjs/next"
|
||||
|
||||
const AuthenticateRedirectLayout: BlitzLayout = ({children}) => {
|
||||
return <div id="layout">{children}</div>
|
||||
}
|
||||
|
||||
AuthenticateRedirectLayout.authenticate = {redirectTo: "/login"}
|
||||
|
||||
export default AuthenticateRedirectLayout
|
||||
@@ -0,0 +1,9 @@
|
||||
import {BlitzLayout} from "@blitzjs/next"
|
||||
|
||||
const RedirectAuthenticatedLayout: BlitzLayout = ({children}) => {
|
||||
return <div id="layout">{children}</div>
|
||||
}
|
||||
|
||||
RedirectAuthenticatedLayout.redirectAuthenticatedTo = "/authenticated-query"
|
||||
|
||||
export default RedirectAuthenticatedLayout
|
||||
10
integration-tests/auth-with-rpc/src/mutations/changeRole.ts
Normal file
10
integration-tests/auth-with-rpc/src/mutations/changeRole.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import {setPublicDataForUser} from "@blitzjs/auth"
|
||||
import {Ctx} from "blitz"
|
||||
|
||||
export default async function changeRole({userId, role}: any, ctx: Ctx) {
|
||||
// create two sessions to be changed
|
||||
await ctx.session.$create({userId, role: "USER"})
|
||||
await ctx.session.$create({userId, role: "USER"})
|
||||
|
||||
await setPublicDataForUser(userId, {role})
|
||||
}
|
||||
6
integration-tests/auth-with-rpc/src/mutations/login.ts
Normal file
6
integration-tests/auth-with-rpc/src/mutations/login.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import {Ctx} from "blitz"
|
||||
|
||||
export default async function login(_: any, ctx: Ctx) {
|
||||
await ctx.session.$create({userId: 1, role: "USER"})
|
||||
return true
|
||||
}
|
||||
6
integration-tests/auth-with-rpc/src/mutations/logout.ts
Normal file
6
integration-tests/auth-with-rpc/src/mutations/logout.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import {Ctx} from "blitz"
|
||||
|
||||
export default async function logout(_: any, ctx: Ctx) {
|
||||
await ctx.session.$revoke()
|
||||
return true
|
||||
}
|
||||
29
integration-tests/auth-with-rpc/src/pages/_app.tsx
Normal file
29
integration-tests/auth-with-rpc/src/pages/_app.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import {withBlitz} from "../blitz-client"
|
||||
import {useQueryErrorResetBoundary} from "@blitzjs/rpc"
|
||||
import {AppProps, ErrorBoundary, ErrorFallbackProps} from "@blitzjs/next"
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
;(window as any).DEBUG_BLITZ = 1
|
||||
}
|
||||
|
||||
export default withBlitz(function App({Component, pageProps}: AppProps) {
|
||||
const getLayout = Component.getLayout || ((page) => page)
|
||||
|
||||
return (
|
||||
<ErrorBoundary
|
||||
FallbackComponent={RootErrorFallback}
|
||||
onReset={useQueryErrorResetBoundary().reset}
|
||||
>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</ErrorBoundary>
|
||||
)
|
||||
})
|
||||
|
||||
function RootErrorFallback({error}: ErrorFallbackProps) {
|
||||
return (
|
||||
<div>
|
||||
<div id="error">{error.name}</div>
|
||||
{error.statusCode} {error.message}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
23
integration-tests/auth-with-rpc/src/pages/_document.tsx
Normal file
23
integration-tests/auth-with-rpc/src/pages/_document.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import Document, {NextScript, Head, Html, Main} from "next/document"
|
||||
|
||||
class MyDocument extends Document {
|
||||
// Only uncomment if you need to customize this behaviour
|
||||
// static async getInitialProps(ctx: DocumentContext) {
|
||||
// const initialProps = await Document.getInitialProps(ctx)
|
||||
// return {...initialProps}
|
||||
// }
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument
|
||||
@@ -0,0 +1,4 @@
|
||||
import {rpcHandler} from "@blitzjs/rpc"
|
||||
import {api} from "../../../blitz-server"
|
||||
|
||||
export default api(rpcHandler({onError: console.log}))
|
||||
@@ -0,0 +1,34 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function AuthenticatedQuery() {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AuthenticatedQuery
|
||||
43
integration-tests/auth-with-rpc/src/pages/custom-plugin.tsx
Normal file
43
integration-tests/auth-with-rpc/src/pages/custom-plugin.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import {Suspense, useEffect} from "react"
|
||||
import {useQuery} from "@blitzjs/rpc"
|
||||
import getNoauthBasic from "../queries/getNoauthBasic"
|
||||
|
||||
const Content = () => {
|
||||
const [result] = useQuery(getNoauthBasic, undefined)
|
||||
return (
|
||||
<>
|
||||
<div id="rpc-result">{result}</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const CustomPlugin: BlitzPage = () => {
|
||||
//send the event to the plugin to create a new session
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
const event = new Event("blitz:session-created")
|
||||
document.dispatchEvent(event)
|
||||
}, 100)
|
||||
setTimeout(() => {
|
||||
const error = new Error("RPC failed")
|
||||
const rpcEvent = new CustomEvent("blitz:rpc-error", {
|
||||
detail: error,
|
||||
})
|
||||
document.dispatchEvent(rpcEvent)
|
||||
}, 2000)
|
||||
})
|
||||
|
||||
return (
|
||||
<div id="root">
|
||||
<div id="page">This is the custom plugin page</div>
|
||||
<div id="before-req"> Initial Content </div>
|
||||
<div id="before-res"> Initial Content </div>
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CustomPlugin
|
||||
@@ -0,0 +1,33 @@
|
||||
import {gSSP} from "../blitz-server"
|
||||
import {getSession} from "@blitzjs/auth"
|
||||
import {GetServerSideProps} from "next"
|
||||
import {Suspense} from "react"
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = gSSP(async ({req, res}) => {
|
||||
const session = await getSession(req, res)
|
||||
await session.$setPublicData({role: "USER"})
|
||||
|
||||
return {
|
||||
props: {},
|
||||
}
|
||||
})
|
||||
|
||||
function Content() {
|
||||
return (
|
||||
<div>
|
||||
<div id="content">it works</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function GSSPSetPublicData() {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default GSSPSetPublicData
|
||||
@@ -0,0 +1,40 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import AuthenticateRedirectLayout from "../layouts/AuthenticateRedirectLayout"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const LayoutAuthenticateLogoutPage: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
LayoutAuthenticateLogoutPage.getLayout = (page) => (
|
||||
<AuthenticateRedirectLayout>{page}</AuthenticateRedirectLayout>
|
||||
)
|
||||
|
||||
export default LayoutAuthenticateLogoutPage
|
||||
@@ -0,0 +1,40 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import AuthenticateRedirectLayout from "../layouts/AuthenticateRedirectLayout"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const LayoutAuthenticateRedirectPage: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading redirect page..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
LayoutAuthenticateRedirectPage.getLayout = (page) => (
|
||||
<AuthenticateRedirectLayout>{page}</AuthenticateRedirectLayout>
|
||||
)
|
||||
|
||||
export default LayoutAuthenticateRedirectPage
|
||||
@@ -0,0 +1,38 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import AuthenticateLayout from "../layouts/AuthenticateLayout"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const LayoutAuthenticatePage: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
LayoutAuthenticatePage.getLayout = (page) => <AuthenticateLayout>{page}</AuthenticateLayout>
|
||||
|
||||
export default LayoutAuthenticatePage
|
||||
@@ -0,0 +1,10 @@
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import RedirectAuthenticatedLayout from "../layouts/RedirectAuthenticatedLayout"
|
||||
|
||||
const LayoutRedirectAuthenticatedPage: BlitzPage = () => <div id="page-container">Hello World</div>
|
||||
|
||||
LayoutRedirectAuthenticatedPage.getLayout = (page) => (
|
||||
<RedirectAuthenticatedLayout>{page}</RedirectAuthenticatedLayout>
|
||||
)
|
||||
|
||||
export default LayoutRedirectAuthenticatedPage
|
||||
@@ -0,0 +1,15 @@
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import AuthenticateLayout from "../layouts/AuthenticateLayout"
|
||||
|
||||
const LayoutUnauthenticatePage: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<p id="content">this should be rendered</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
LayoutUnauthenticatePage.getLayout = (page) => <AuthenticateLayout>{page}</AuthenticateLayout>
|
||||
LayoutUnauthenticatePage.authenticate = false
|
||||
|
||||
export default LayoutUnauthenticatePage
|
||||
62
integration-tests/auth-with-rpc/src/pages/login.tsx
Normal file
62
integration-tests/auth-with-rpc/src/pages/login.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import {useRouter} from "next/router"
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import login from "../mutations/login"
|
||||
import logout from "../mutations/logout"
|
||||
import getCurrentUser from "../queries/getCurrentUser"
|
||||
import {Suspense, useState} from "react"
|
||||
|
||||
function Content() {
|
||||
const router = useRouter()
|
||||
const [error, setError] = useState(null)
|
||||
const [userId] = useQuery(getCurrentUser, null)
|
||||
const [loginMutation] = useMutation(login)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
|
||||
if (error) return <div id="error">{error}</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{userId ? "logged-in" : "logged-out"}</div>
|
||||
{userId ? (
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
try {
|
||||
await logoutMutation()
|
||||
} catch (error: any) {
|
||||
setError(error.toString())
|
||||
}
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
id="login"
|
||||
onClick={async () => {
|
||||
await loginMutation()
|
||||
|
||||
const next = router.query.next ? decodeURIComponent(router.query.next as string) : null
|
||||
if (next) {
|
||||
await router.push(next)
|
||||
}
|
||||
}}
|
||||
>
|
||||
login
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Login() {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback="Loading...">
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Login
|
||||
20
integration-tests/auth-with-rpc/src/pages/noauth-query.tsx
Normal file
20
integration-tests/auth-with-rpc/src/pages/noauth-query.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import {useQuery} from "@blitzjs/rpc"
|
||||
import getNoauthBasic from "../queries/getNoauthBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getNoauthBasic, undefined)
|
||||
return <div id="content">{result}</div>
|
||||
}
|
||||
|
||||
function NoAuthQuery() {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NoAuthQuery
|
||||
@@ -0,0 +1,37 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const AuthLogout: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
AuthLogout.authenticate = {redirectTo: "/login"}
|
||||
|
||||
export default AuthLogout
|
||||
@@ -0,0 +1,37 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const AuthRedirect: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading redirect page..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
AuthRedirect.authenticate = {redirectTo: "/login"}
|
||||
|
||||
export default AuthRedirect
|
||||
@@ -0,0 +1,37 @@
|
||||
import {useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import {BlitzPage} from "@blitzjs/next"
|
||||
import logout from "../mutations/logout"
|
||||
import getAuthenticatedBasic from "../queries/getAuthenticatedBasic"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getAuthenticatedBasic, undefined)
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
<div>
|
||||
<div id="content">{result}</div>
|
||||
<button
|
||||
id="logout"
|
||||
onClick={async () => {
|
||||
await logoutMutation()
|
||||
}}
|
||||
>
|
||||
logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Authenticate: BlitzPage = () => {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Authenticate.authenticate = true
|
||||
|
||||
export default Authenticate
|
||||
43
integration-tests/auth-with-rpc/src/pages/prefetching.tsx
Normal file
43
integration-tests/auth-with-rpc/src/pages/prefetching.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import {gSSP} from "../blitz-server"
|
||||
import {GetServerSidePropsContext} from "next"
|
||||
import {dehydrate, getQueryClient, getQueryKey, QueryClient, useQuery} from "@blitzjs/rpc"
|
||||
import getNoauthBasic from "../queries/getNoauthBasic"
|
||||
import {Suspense} from "react"
|
||||
import {SessionContext} from "@blitzjs/auth"
|
||||
|
||||
function Content() {
|
||||
const [result] = useQuery(getNoauthBasic, null, {
|
||||
staleTime: 60 * 1000,
|
||||
})
|
||||
|
||||
return <div id="content">{result}</div>
|
||||
}
|
||||
|
||||
function Bomb() {
|
||||
return <div id="content">somebody set up us the bomb</div>
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={<Bomb />}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type Props = {
|
||||
dehydratedState: any
|
||||
}
|
||||
|
||||
export const getServerSideProps = gSSP<Props>(async ({ctx}) => {
|
||||
await getQueryClient().prefetchQuery(getQueryKey(getNoauthBasic, null), () =>
|
||||
getNoauthBasic(null, ctx),
|
||||
)
|
||||
return {
|
||||
props: {
|
||||
dehydratedState: dehydrate(queryClient),
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,7 @@
|
||||
function RedirectAuthenticated() {
|
||||
return <div id="page-container">Hello World</div>
|
||||
}
|
||||
|
||||
RedirectAuthenticated.redirectAuthenticatedTo = "/authenticated-query"
|
||||
|
||||
export default RedirectAuthenticated
|
||||
@@ -0,0 +1,39 @@
|
||||
import {invalidateQuery, useMutation, useQuery} from "@blitzjs/rpc"
|
||||
import changeRole from "../mutations/changeRole"
|
||||
import getPublicDataForUser from "../queries/getPublicDataForUser"
|
||||
import {Suspense} from "react"
|
||||
|
||||
function Content() {
|
||||
const [publicData] = useQuery(getPublicDataForUser, {userId: 1})
|
||||
return (
|
||||
<div id="session">
|
||||
<>
|
||||
<div className="userId">userId: {publicData.userId}</div>
|
||||
<div className="role">role: {publicData.role}</div>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SetPublicData() {
|
||||
const [changeRoleMutation] = useMutation(changeRole)
|
||||
|
||||
return (
|
||||
<div id="page">
|
||||
<button
|
||||
id="change-role"
|
||||
onClick={async () => {
|
||||
await changeRoleMutation({userId: 1, role: "new role"})
|
||||
await invalidateQuery(getPublicDataForUser)
|
||||
}}
|
||||
>
|
||||
Set new role for user
|
||||
</button>
|
||||
<Suspense fallback="">
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SetPublicData
|
||||
@@ -0,0 +1,8 @@
|
||||
import {Ctx} from "blitz"
|
||||
import delay from "delay"
|
||||
|
||||
export default async function getAuthenticatedBasic(_: any, ctx: Ctx) {
|
||||
await delay(10)
|
||||
ctx.session.$authorize()
|
||||
return "authenticated-basic-result"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import {Ctx} from "blitz"
|
||||
import delay from "delay"
|
||||
|
||||
export default async function getNoauthBasic(_: any, ctx: Ctx) {
|
||||
await delay(10)
|
||||
return ctx.session.userId
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import {Ctx} from "blitz"
|
||||
import delay from "delay"
|
||||
|
||||
export default async function getNoauthBasic(_: any, ctx: Ctx) {
|
||||
await delay(10)
|
||||
return "noauth-basic-result"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import {Ctx} from "blitz"
|
||||
import db from "../../db"
|
||||
|
||||
export default async function getPublicDataForUser({userId}: any, ctx: Ctx) {
|
||||
const role = ctx.session.role
|
||||
return {
|
||||
userId,
|
||||
role,
|
||||
}
|
||||
}
|
||||
291
integration-tests/auth-with-rpc/test/index.test.ts
Normal file
291
integration-tests/auth-with-rpc/test/index.test.ts
Normal file
@@ -0,0 +1,291 @@
|
||||
import {describe, it, expect, beforeAll, afterAll} from "vitest"
|
||||
import {
|
||||
killApp,
|
||||
findPort,
|
||||
runBlitzCommand,
|
||||
blitzLaunchApp,
|
||||
blitzBuild,
|
||||
blitzStart,
|
||||
waitFor,
|
||||
} from "../../utils/next-test-utils"
|
||||
import webdriver from "../../utils/next-webdriver"
|
||||
|
||||
let app: any
|
||||
let appPort: number
|
||||
|
||||
const runTests = () => {
|
||||
describe("Auth", () => {
|
||||
describe("custom plugin", () => {
|
||||
it("custom plugin - events", async () => {
|
||||
const browser = await webdriver(appPort, "/custom-plugin")
|
||||
let text = await browser.elementByCss("#page").text()
|
||||
await waitFor(250)
|
||||
text = await browser.elementByCss("#page").text()
|
||||
expect(text).toBe("Custom plugin Session Created")
|
||||
await waitFor(2000)
|
||||
text = await browser.elementByCss("#page").text()
|
||||
expect(text).toBe("Custom plugin RPC Error")
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
})
|
||||
it("custom plugin - middleware", async () => {
|
||||
const browser = await webdriver(appPort, "/custom-plugin")
|
||||
await waitFor(100)
|
||||
let text = await browser.elementByCss("#before-req").text()
|
||||
expect(text).toBe("customHeaderValue")
|
||||
await waitFor(1000)
|
||||
text = await browser.elementByCss("#before-res").text()
|
||||
expect(text).toBe("55")
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
})
|
||||
})
|
||||
describe("unauthenticated", () => {
|
||||
it("should render result for open query", async () => {
|
||||
const browser = await webdriver(appPort, "/noauth-query")
|
||||
let text = await browser.elementByCss("#page").text()
|
||||
await browser.waitForElementByCss("#content")
|
||||
text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/noauth-basic-result/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("should render error for protected query", async () => {
|
||||
const browser = await webdriver(appPort, "/authenticated-query")
|
||||
await browser.waitForElementByCss("#error")
|
||||
let text = await browser.elementByCss("#error").text()
|
||||
expect(text).toMatch(/AuthenticationError/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("should render error for protected page", async () => {
|
||||
const browser = await webdriver(appPort, "/page-dot-authenticate")
|
||||
await browser.waitForElementByCss("#error")
|
||||
let text = await browser.elementByCss("#error").text()
|
||||
expect(text).toMatch(/AuthenticationError/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("should render error for protected layout", async () => {
|
||||
const browser = await webdriver(appPort, "/layout-authenticate")
|
||||
await browser.waitForElementByCss("#error")
|
||||
let text = await browser.elementByCss("#error").text()
|
||||
expect(text).toMatch(/AuthenticationError/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("should render Page.authenticate = false even when Layout.authenticate = true", async () => {
|
||||
const browser = await webdriver(appPort, "/layout-unauthenticate")
|
||||
await browser.waitForElementByCss("#content")
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/this should be rendered/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe("authenticated", () => {
|
||||
it("should login and out successfully", async () => {
|
||||
const browser = await webdriver(appPort, "/login")
|
||||
await browser.waitForElementByCss("#content")
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/logged-out/)
|
||||
await browser.elementByCss("#login").click()
|
||||
await waitFor(200)
|
||||
text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/logged-in/)
|
||||
await browser.elementByCss("#logout").click()
|
||||
await waitFor(250)
|
||||
text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/logged-out/)
|
||||
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("should logout without infinite loop #2233", async () => {
|
||||
// Login
|
||||
let browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
await browser.elementByCss("#login").click()
|
||||
await waitFor(200)
|
||||
|
||||
await browser.eval(`window.location = "/authenticated-query"`)
|
||||
await browser.waitForElementByCss("#content")
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/authenticated-basic-result/)
|
||||
await browser.elementByCss("#logout").click()
|
||||
await waitFor(200)
|
||||
await browser.waitForElementByCss("#error")
|
||||
text = await browser.elementByCss("#error").text()
|
||||
expect(text).toMatch(/AuthenticationError/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("Page.authenticate = {redirect} should work ", async () => {
|
||||
// Login
|
||||
let browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
await browser.elementByCss("#login").click()
|
||||
await waitFor(200)
|
||||
|
||||
await browser.eval(`window.location = "/page-dot-authenticate-redirect"`)
|
||||
await browser.waitForElementByCss("#content")
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/authenticated-basic-result/)
|
||||
await browser.elementByCss("#logout").click()
|
||||
await waitFor(500)
|
||||
|
||||
expect(await browser.url()).toMatch(/\/login/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
|
||||
it("Layout.authenticate = {redirect} should work ", async () => {
|
||||
// Login
|
||||
let browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
await browser.elementByCss("#login").click()
|
||||
await waitFor(200)
|
||||
|
||||
await browser.eval(`window.location = "/layout-authenticate-redirect"`)
|
||||
await browser.waitForElementByCss("#content")
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/authenticated-basic-result/)
|
||||
await browser.elementByCss("#logout").click()
|
||||
await waitFor(500)
|
||||
|
||||
expect(await browser.url()).toMatch(/\/login/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe("prefetching", () => {
|
||||
it("should prefetch from the query cache #2281", async () => {
|
||||
const browser = await webdriver(appPort, "/prefetching")
|
||||
await waitFor(100)
|
||||
await browser.waitForElementByCss("#content")
|
||||
const text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/noauth-basic-result/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe("setting public data for a user", () => {
|
||||
it("should update all sessions of the user", async () => {
|
||||
// Ensure logged out
|
||||
const browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
if (text.match(/logged-in/)) {
|
||||
await browser.elementByCss("#logout").click()
|
||||
await waitFor(200)
|
||||
}
|
||||
|
||||
await browser.eval(`window.location = "/set-public-data"`)
|
||||
await browser.waitForElementByCss("#change-role")
|
||||
await browser.elementByCss("#change-role").click()
|
||||
await waitFor(500)
|
||||
await browser.waitForElementByCss(".role")
|
||||
text = await browser.elementByCss(".role").text()
|
||||
expect(text).toMatch(/role: new role/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe("Page.redirectAuthenticatedTo", () => {
|
||||
it("should work when redirecting to page with useQuery", async () => {
|
||||
// https://github.com/blitz-js/legacy-framework/issues/2527
|
||||
|
||||
// Ensure logged in
|
||||
const browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
if (text.match(/logged-out/)) {
|
||||
await browser.elementByCss("#login").click()
|
||||
await waitFor(200)
|
||||
}
|
||||
|
||||
await browser.eval(`window.location = "/redirect-authenticated"`)
|
||||
await browser.waitForElementByCss("#content")
|
||||
text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/authenticated-basic-result/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe("Layout.redirectAuthenticatedTo", () => {
|
||||
it("should work when redirecting to page with useQuery", async () => {
|
||||
// https://github.com/blitz-js/legacy-framework/issues/2527
|
||||
|
||||
// Ensure logged in
|
||||
const browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
if (text.match(/logged-out/)) {
|
||||
await browser.elementByCss("#login").click()
|
||||
await waitFor(200)
|
||||
}
|
||||
|
||||
await browser.eval(`window.location = "/layout-redirect-authenticated"`)
|
||||
await browser.waitForElementByCss("#content")
|
||||
text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/authenticated-basic-result/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe("setPublicData", () => {
|
||||
it("it should not throw CSRF error", async () => {
|
||||
// https://github.com/blitz-js/legacy-framework/issues/2448
|
||||
|
||||
// ensure logged out
|
||||
const browser = await webdriver(appPort, "/login")
|
||||
await waitFor(200)
|
||||
let text = await browser.elementByCss("#content").text()
|
||||
if (text.match(/logged-in/)) {
|
||||
await browser.elementByCss("#logout").click()
|
||||
await waitFor(200)
|
||||
}
|
||||
|
||||
await browser.eval(`window.location = "/gssp-setpublicdata"`)
|
||||
await browser.waitForElementByCss("#content")
|
||||
text = await browser.elementByCss("#content").text()
|
||||
expect(text).toMatch(/it works/)
|
||||
if (browser) await browser.close()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
describe("Auth Tests", () => {
|
||||
describe("dev mode", () => {
|
||||
beforeAll(async () => {
|
||||
try {
|
||||
await runBlitzCommand(["prisma", "migrate", "reset", "--force"])
|
||||
appPort = await findPort()
|
||||
app = await blitzLaunchApp(appPort, {cwd: process.cwd()})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}, 5000 * 60 * 2)
|
||||
afterAll(async () => await killApp(app))
|
||||
runTests()
|
||||
})
|
||||
|
||||
describe("server mode", () => {
|
||||
beforeAll(async () => {
|
||||
try {
|
||||
await runBlitzCommand(["prisma", "generate"])
|
||||
await runBlitzCommand(["prisma", "migrate", "deploy"])
|
||||
await blitzBuild()
|
||||
appPort = await findPort()
|
||||
app = await blitzStart(appPort, {cwd: process.cwd()})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}, 5000 * 60 * 2)
|
||||
afterAll(async () => await killApp(app))
|
||||
|
||||
runTests()
|
||||
})
|
||||
})
|
||||
11
integration-tests/auth-with-rpc/tsconfig.json
Normal file
11
integration-tests/auth-with-rpc/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "@blitzjs/config/tsconfig.nextjs.json",
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "types"],
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"react": ["./node_modules/@types/react"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"baseUrl": "."
|
||||
}
|
||||
9
integration-tests/auth-with-rpc/vitest.config.ts
Normal file
9
integration-tests/auth-with-rpc/vitest.config.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/// <reference types="vitest" />
|
||||
import {defineConfig} from "vitest/config"
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
testTimeout: 100000,
|
||||
hookTimeout: 100000,
|
||||
},
|
||||
})
|
||||
@@ -17,11 +17,11 @@
|
||||
"prisma:studio": "prisma studio"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "workspace:*",
|
||||
"@blitzjs/config": "workspace:*",
|
||||
"@blitzjs/next": "workspace:*",
|
||||
"@blitzjs/auth": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/config": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/next": "workspace:2.0.0-beta.20",
|
||||
"@prisma/client": "4.6.0",
|
||||
"blitz": "workspace:2.0.0-beta.17",
|
||||
"blitz": "workspace:2.0.0-beta.20",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "12.2.5",
|
||||
"prisma": "4.6.0",
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
"schema": "db/schema.prisma"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "workspace:*",
|
||||
"@blitzjs/next": "workspace:*",
|
||||
"@blitzjs/rpc": "workspace:*",
|
||||
"@blitzjs/auth": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/next": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/rpc": "workspace:2.0.0-beta.20",
|
||||
"@prisma/client": "4.6.0",
|
||||
"blitz": "workspace:*",
|
||||
"blitz": "workspace:2.0.0-beta.20",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "12.2.5",
|
||||
"prisma": "4.6.0",
|
||||
@@ -28,7 +28,7 @@
|
||||
"react-dom": "18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "workspace:*",
|
||||
"@blitzjs/config": "workspace:2.0.0-beta.20",
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/config": "workspace:*",
|
||||
"@blitzjs/next": "workspace:*",
|
||||
"@blitzjs/rpc": "workspace:*",
|
||||
"blitz": "workspace:*",
|
||||
"@blitzjs/config": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/next": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/rpc": "workspace:2.0.0-beta.20",
|
||||
"blitz": "workspace:2.0.0-beta.20",
|
||||
"next": "12.2.5",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export default async function getNoSuspenseBasic() {
|
||||
return "basic-result"
|
||||
}
|
||||
@@ -16,11 +16,11 @@
|
||||
"prisma:studio": "prisma studio"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "workspace:*",
|
||||
"@blitzjs/next": "workspace:*",
|
||||
"@blitzjs/rpc": "workspace:*",
|
||||
"@blitzjs/auth": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/next": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/rpc": "workspace:2.0.0-beta.20",
|
||||
"@prisma/client": "4.6.0",
|
||||
"blitz": "workspace:*",
|
||||
"blitz": "workspace:2.0.0-beta.20",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "12.2.5",
|
||||
"prisma": "4.6.0",
|
||||
@@ -28,7 +28,7 @@
|
||||
"react-dom": "18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "workspace:*",
|
||||
"@blitzjs/config": "workspace:2.0.0-beta.20",
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import getBasic from "../app/queries/getBasic"
|
||||
import getNoSuspenseBasic from "../app/queries/getNoSuspenseBasic"
|
||||
import {useQuery} from "@blitzjs/rpc"
|
||||
import React from "react"
|
||||
|
||||
function Content() {
|
||||
const [result, {isFetching}] = useQuery(getBasic, undefined)
|
||||
const [result, {isFetching}] = useQuery(getNoSuspenseBasic, undefined)
|
||||
|
||||
if (isFetching) {
|
||||
return <>Loading...</>
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
"clean": "rm -rf .turbo && rm -rf node_modules"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "workspace:*",
|
||||
"@blitzjs/config": "workspace:*",
|
||||
"@blitzjs/next": "workspace:*",
|
||||
"@blitzjs/rpc": "workspace:*",
|
||||
"@blitzjs/auth": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/config": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/next": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/rpc": "workspace:2.0.0-beta.20",
|
||||
"@prisma/client": "4.6.0",
|
||||
"@tanstack/react-query": "4.0.10",
|
||||
"blitz": "workspace:*",
|
||||
"blitz": "workspace:2.0.0-beta.20",
|
||||
"next": "12.2.5",
|
||||
"prisma": "4.6.0",
|
||||
"react": "18.2.0",
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import {BlitzRpcPlugin} from "@blitzjs/rpc"
|
||||
import {setupBlitzClient} from "@blitzjs/next"
|
||||
import {AuthClientPlugin} from "@blitzjs/auth"
|
||||
|
||||
const {withBlitz} = setupBlitzClient({
|
||||
plugins: [
|
||||
AuthClientPlugin({
|
||||
cookiePrefix: "react-query-utils-tests-cookie-prefix",
|
||||
}),
|
||||
BlitzRpcPlugin({}),
|
||||
],
|
||||
plugins: [BlitzRpcPlugin({})],
|
||||
})
|
||||
|
||||
export {withBlitz}
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
import {setupBlitzServer} from "@blitzjs/next"
|
||||
import {AuthServerPlugin, PrismaStorage} from "@blitzjs/auth"
|
||||
import {simpleRolesIsAuthorized} from "@blitzjs/auth"
|
||||
import db from "../db"
|
||||
import {BlitzLogger} from "blitz"
|
||||
|
||||
const {gSSP, gSP, api} = setupBlitzServer({
|
||||
plugins: [
|
||||
AuthServerPlugin({
|
||||
cookiePrefix: "react-query-utils-tests-cookie-prefix",
|
||||
storage: PrismaStorage(db),
|
||||
isAuthorized: simpleRolesIsAuthorized,
|
||||
}),
|
||||
],
|
||||
plugins: [],
|
||||
logger: BlitzLogger({}),
|
||||
})
|
||||
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
const {withBlitz} = require("@blitzjs/next")
|
||||
module.exports = withBlitz({})
|
||||
module.exports = withBlitz({
|
||||
blitz: {
|
||||
includeRPCFolders: ["../no-suspense/app"],
|
||||
},
|
||||
})
|
||||
|
||||
@@ -16,11 +16,10 @@
|
||||
"schema": "db/schema.prisma"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "workspace:*",
|
||||
"@blitzjs/next": "workspace:*",
|
||||
"@blitzjs/rpc": "workspace:*",
|
||||
"@blitzjs/next": "workspace:2.0.0-beta.20",
|
||||
"@blitzjs/rpc": "workspace:2.0.0-beta.20",
|
||||
"@prisma/client": "4.6.0",
|
||||
"blitz": "workspace:*",
|
||||
"blitz": "workspace:2.0.0-beta.20",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "12.2.5",
|
||||
"prisma": "4.6.0",
|
||||
@@ -28,7 +27,7 @@
|
||||
"react-dom": "18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "workspace:*",
|
||||
"@blitzjs/config": "workspace:2.0.0-beta.20",
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import {getQueryData, useQuery} from "@blitzjs/rpc"
|
||||
import {Suspense, useState} from "react"
|
||||
import getNoSuspenseBasic from "../../no-suspense/app/queries/getNoSuspenseBasic"
|
||||
|
||||
function Content() {
|
||||
const [data] = useQuery(getNoSuspenseBasic, undefined)
|
||||
const [newData, setNewData] = useState<string>()
|
||||
return (
|
||||
<div>
|
||||
<div>{data}</div>
|
||||
{newData && <div id="new-data">{newData}</div>}
|
||||
<button
|
||||
id="button"
|
||||
onClick={async () => {
|
||||
const newData = getQueryData(getNoSuspenseBasic, undefined)
|
||||
setNewData(newData)
|
||||
}}
|
||||
>
|
||||
Call getQueryData
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function PageWithGetQueryData() {
|
||||
return (
|
||||
<div id="page">
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PageWithGetQueryData
|
||||
@@ -32,6 +32,25 @@ const runTests = () => {
|
||||
)
|
||||
})
|
||||
|
||||
describe("get query data from monorepo", () => {
|
||||
it(
|
||||
"should work",
|
||||
async () => {
|
||||
const browser = await webdriver(appPort, "/page-with-monorepo-query")
|
||||
|
||||
browser.waitForElementByCss("#button", 0)
|
||||
await browser.elementByCss("#button").click()
|
||||
|
||||
browser.waitForElementByCss("#new-data", 0)
|
||||
const newText = await browser.elementByCss("#new-data").text()
|
||||
expect(newText).toMatch(/basic-result/)
|
||||
|
||||
if (browser) await browser.close()
|
||||
},
|
||||
5000 * 60 * 2,
|
||||
)
|
||||
})
|
||||
|
||||
describe("prefetch infinite query", () => {
|
||||
it(
|
||||
"should work",
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export default async function setBasic(input, ctx) {
|
||||
global.basic = input
|
||||
return global.basic
|
||||
}
|
||||
12
integration-tests/rpc-path-root/app/queries/getBasic.js
Normal file
12
integration-tests/rpc-path-root/app/queries/getBasic.js
Normal file
@@ -0,0 +1,12 @@
|
||||
if (typeof window !== "undefined") {
|
||||
throw new Error("This should not be loaded on the client")
|
||||
}
|
||||
|
||||
export default async function getBasic() {
|
||||
if (typeof window !== "undefined") {
|
||||
throw new Error("This should not be loaded on the client")
|
||||
}
|
||||
|
||||
global.basic ??= "basic-result"
|
||||
return global.basic
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
export default async function getBasic() {
|
||||
return "basic-result"
|
||||
return "nested-basic"
|
||||
}
|
||||
5
integration-tests/rpc-path-root/next-env.d.ts
vendored
Normal file
5
integration-tests/rpc-path-root/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
7
integration-tests/rpc-path-root/next.config.js
Normal file
7
integration-tests/rpc-path-root/next.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const {withBlitz} = require("@blitzjs/next")
|
||||
module.exports = withBlitz({
|
||||
blitz: {
|
||||
resolverPath: "root",
|
||||
includeRPCFolders: ["../no-suspense/app"],
|
||||
},
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user