Skip to content

Commit a3727f8

Browse files
add redis receiver component and add a e2e test for it
1 parent 31bca68 commit a3727f8

File tree

9 files changed

+200
-43
lines changed

9 files changed

+200
-43
lines changed

.circleci/config.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,51 @@ jobs:
343343
- store_artifacts:
344344
path: react_on_rails_pro/spec/dummy/yarn-error.log
345345

346+
# TODO: DRY with previous job
347+
dummy-app-node-renderer-e2-tests:
348+
docker:
349+
- image: *docker_image
350+
steps:
351+
- checkout
352+
- run: *print-system-info
353+
- restore_cache: *restore-package-gem-cache
354+
- restore_cache: *restore-package-node-modules-cache
355+
- restore_cache: *restore-dummy-app-node-modules-cache
356+
- restore_cache: *restore-dummy-app-gem-cache
357+
- run: rm -rf react_on_rails_pro/spec/dummy/public/webpack
358+
- run: rm -rf react_on_rails_pro/spec/dummy/ssr-generated
359+
- restore_cache: *restore-dummy-app-webpack-bundle-cache
360+
- run: *install-dummy-app-ruby-gems
361+
- run: *install-package-node-modules
362+
- run: *install-latest-chrome
363+
- run: *install-dummy-app-node-modules
364+
- run:
365+
name: Generate file-system based entrypoints (Pro)
366+
working_directory: react_on_rails_pro
367+
command: cd spec/dummy && bundle exec rake react_on_rails:generate_packs
368+
- run:
369+
name: Run Pro Node renderer in a background
370+
working_directory: react_on_rails_pro
371+
command: cd spec/dummy && yarn run node-renderer
372+
background: true
373+
- run:
374+
name: run rails server in background (Pro dummy app)
375+
working_directory: react_on_rails_pro
376+
command: cd spec/dummy && RAILS_ENV=test rails server
377+
background: true
378+
- run:
379+
name: wait for rails server to start
380+
command: |
381+
while ! curl -s http://localhost:3000 > /dev/null; do sleep 1; done
382+
- run:
383+
name: Run playwright tests (Pro dummy app)
384+
working_directory: react_on_rails_pro/spec/dummy
385+
command: yarn e2e-test
386+
- store_test_results:
387+
path: react_on_rails_pro/spec/dummy/results.xml
388+
- store_artifacts:
389+
path: react_on_rails_pro/spec/dummy/playwright-report
390+
346391
workflows:
347392
version: 2
348393
build-and-test:
Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,51 @@
1-
import { test, expect } from '@playwright/test';
1+
import { randomUUID } from 'crypto';
2+
import { test, expect, Page } from '@playwright/test';
3+
import { createClient } from 'redis';
4+
5+
const createRedisClient = async () => {
6+
const url = process.env.REDIS_URL || 'redis://localhost:6379';
7+
const client = createClient({ url });
8+
await client.connect();
9+
return client;
10+
}
11+
12+
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
13+
14+
const assertPageState = async(page: Page, sentValues: Number[]) => {
15+
const nonSentValues = [1,2,3,4,5].filter(v => !sentValues.includes(v));
16+
17+
await Promise.all(sentValues.map(async (v) => {
18+
await expect(page.getByText(`Value of "Item${v}": Value${v}`)).toBeVisible();
19+
await expect(page.getByText(`Waiting for the key "Item${v}"`)).not.toBeVisible();
20+
}));
21+
22+
await Promise.all(nonSentValues.map(async (v) => {
23+
await expect(page.getByText(`Value of "Item${v}": Value${v}`)).not.toBeVisible()
24+
await expect(page.getByText(`Waiting for the key "Item${v}"`)).toBeVisible()
25+
}));
26+
}
227

328
test('has title', async ({ page }) => {
4-
await page.goto('https://playwright.dev/');
29+
const requestId = randomUUID();
30+
await page.goto(`http://localhost:3000/redis_receiver_for_testing?request_id=${requestId}`, { waitUntil: "commit" });
531

6-
// Expect a title "to contain" a substring.
7-
await expect(page).toHaveTitle(/Playwright/);
8-
});
32+
const sentValues: Number[] = [];
33+
await assertPageState(page, sentValues);
34+
35+
const redisClient = await createRedisClient();
36+
redisClient.xAdd(`stream:${requestId}`, '*', { ':Item1': JSON.stringify('Value1') });
37+
sentValues.push(1);
38+
await assertPageState(page, sentValues);
939

10-
test('get started link', async ({ page }) => {
11-
await page.goto('https://playwright.dev/');
40+
redisClient.xAdd(`stream:${requestId}`, '*', { ':Item4': JSON.stringify('Value4') });
41+
sentValues.push(4);
42+
await assertPageState(page, sentValues);
1243

13-
// Click the get started link.
14-
await page.getByRole('link', { name: 'Get started' }).click();
44+
redisClient.xAdd(`stream:${requestId}`, '*', { ':Item2': JSON.stringify('Value2') });
45+
sentValues.push(2);
46+
await assertPageState(page, sentValues);
1547

16-
// Expects page to have a heading with the name of Installation.
17-
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
48+
redisClient.xAdd(`stream:${requestId}`, '*', { ':Item3': JSON.stringify('Value3') });
49+
sentValues.push(3);
50+
await assertPageState(page, sentValues);
1851
});

react_on_rails_pro/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
"pino-pretty": "^13.0.0",
9191
"prettier": "^3.2.5",
9292
"react-on-rails": "link:.yalc/react-on-rails",
93-
"redis": "^5.0.1",
93+
"redis": "^5.8.3",
9494
"release-it": "^17.6.0",
9595
"sentry-testkit": "^5.0.6",
9696
"touch": "^3.1.0",
@@ -122,6 +122,7 @@
122122
"postinstall": "test -f post-yarn-install.local && ./post-yarn-install.local || true",
123123
"link-source": "cd ../packages/react-on-rails && yarn && yalc publish",
124124
"test": "nps test",
125+
"e2e-test": "playwright test",
125126
"prepack": "nps build.prepack",
126127
"prepare": "nps build.prepack",
127128
"prepublishOnly": "nps build",

react_on_rails_pro/playwright.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ export default defineConfig({
2222
/* Opt out of parallel tests on CI. */
2323
workers: process.env.CI ? 1 : undefined,
2424
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
25-
reporter: 'html',
25+
reporter: process.env.ci ? [
26+
['html'],
27+
['junit', { outputFile: 'test-results/results.xml' }]
28+
] : 'html',
2629
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
2730
use: {
2831
/* Base URL to use in actions like `await page.goto('')`. */

react_on_rails_pro/spec/dummy/app/controllers/pages_controller.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,36 @@ def rsc_posts_page_over_redis
6969
raise "Redis thread timed out"
7070
end
7171

72+
def redis_receiver
73+
@request_id = SecureRandom.uuid
74+
75+
redis_thread = Thread.new do
76+
redis = ::Redis.new
77+
5.times do |index|
78+
sleep 1
79+
redis.xadd("stream:#{@request_id}", { ":Item#{index + 1}" => "Value of Item#{index + 1}".to_json })
80+
end
81+
rescue StandardError => e
82+
Rails.logger.error "Error writing Items to Redis: #{e.message}"
83+
Rails.logger.error e.backtrace.join("\n")
84+
raise e
85+
end
86+
87+
stream_view_containing_react_components(template: "/pages/redis_receiver")
88+
89+
return if redis_thread.join(10)
90+
91+
Rails.logger.error "Redis thread timed out"
92+
raise "Redis thread timed out"
93+
end
94+
95+
def redis_receiver_for_testing
96+
@request_id = params[:request_id]
97+
raise "request_id is required at the url" if @request_id.empty?
98+
99+
stream_view_containing_react_components(template: "/pages/redis_receiver")
100+
end
101+
72102
def async_on_server_sync_on_client
73103
@render_on_server = true
74104
stream_view_containing_react_components(template: "/pages/async_on_server_sync_on_client")
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<%= stream_react_component("RedisReceiver",
2+
props: { requestId: @request_id },
3+
prerender: true,
4+
trace: true,
5+
id: "RedisReceiver-react-component-0") %>
6+
<hr/>
7+
8+
<h1>React Rails Server Streaming Redis Receiver</h1>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React, { Suspense } from 'react';
2+
import { listenToRequestData } from '../utils/redisReceiver';
3+
import { ErrorBoundary } from '../components/ErrorBoundary';
4+
5+
const RedisItem = async ({ getValue, redisKey }) => {
6+
const value = await getValue(redisKey);
7+
return <p>Value of "{redisKey}": {value}</p>
8+
}
9+
10+
const RedisItemWithWrapper = ({ getValue, redisKey }) => (
11+
<Suspense fallback={<p>Waiting for the key "{redisKey}"</p>}>
12+
<RedisItem getValue={getValue} redisKey={redisKey} />
13+
</Suspense>
14+
)
15+
16+
const RedisReceiver = ({ requestId }, railsContext) => {
17+
const { getValue, close } = listenToRequestData(requestId);
18+
19+
if ('addPostSSRHook' in railsContext) {
20+
railsContext.addPostSSRHook(close);
21+
}
22+
23+
return (
24+
<ErrorBoundary>
25+
<h1>A list of items received from Redis:</h1>
26+
<ol>
27+
{
28+
[1,2,3,4,5].map(index => <RedisItemWithWrapper key={index} getValue={getValue} redisKey={`Item${index}`} />)
29+
}
30+
</ol>
31+
</ErrorBoundary>
32+
)
33+
}
34+
35+
export default RedisReceiver;

react_on_rails_pro/spec/dummy/config/routes.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
get "apollo_graphql" => "pages#apollo_graphql", as: :apollo_graphql
2121
get "lazy_apollo_graphql" => "pages#lazy_apollo_graphql", as: :lazy_apollo_graphql
2222
get "console_logs_in_async_server" => "pages#console_logs_in_async_server", as: :console_logs_in_async_server
23+
get "redis_receiver" => "pages#redis_receiver", as: :redis_receiver
24+
get "redis_receiver_for_testing" => "pages#redis_receiver_for_testing", as: :redis_receiver_for_testing
2325
get "stream_async_components" => "pages#stream_async_components", as: :stream_async_components
2426
get "stream_async_components_for_testing" => "pages#stream_async_components_for_testing",
2527
as: :stream_async_components_for_testing

react_on_rails_pro/yarn.lock

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,32 +1604,32 @@
16041604
"@pnpm/network.ca-file" "^1.0.1"
16051605
config-chain "^1.1.11"
16061606

1607-
"@redis/bloom@5.0.1":
1608-
version "5.0.1"
1609-
resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-5.0.1.tgz#5f4c6bb2cce3908a4c3d246c69187321db9e1818"
1610-
integrity sha512-F7L+rnuJvq/upKaVoEgsf8VT7g5pLQYWRqSUOV3uO4vpVtARzSKJ7CLyJjVsQS+wZVCGxsLMh8DwAIDcny1B+g==
1607+
"@redis/bloom@5.8.3":
1608+
version "5.8.3"
1609+
resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-5.8.3.tgz#c0f04cba102eeb3a68792a599f7c320915eba366"
1610+
integrity sha512-1eldTzHvdW3Oi0TReb8m1yiFt8ZwyF6rv1NpZyG5R4TpCwuAdKQetBKoCw7D96tNFgsVVd6eL+NaGZZCqhRg4g==
16111611

1612-
"@redis/client@5.0.1":
1613-
version "5.0.1"
1614-
resolved "https://registry.yarnpkg.com/@redis/client/-/client-5.0.1.tgz#d39d9c0114b865e2186e46b6d891701f46b293ad"
1615-
integrity sha512-k0EJvlMGEyBqUD3orKe0UMZ66fPtfwqPIr+ZSd853sXj2EyhNtPXSx+J6sENXJNgAlEBhvD+57Dwt0qTisKB0A==
1612+
"@redis/client@5.8.3":
1613+
version "5.8.3"
1614+
resolved "https://registry.yarnpkg.com/@redis/client/-/client-5.8.3.tgz#186ebdd30ff874eae35b8d0ea3151137c22802be"
1615+
integrity sha512-MZVUE+l7LmMIYlIjubPosruJ9ltSLGFmJqsXApTqPLyHLjsJUSAbAJb/A3N34fEqean4ddiDkdWzNu4ZKPvRUg==
16161616
dependencies:
16171617
cluster-key-slot "1.1.2"
16181618

1619-
"@redis/json@5.0.1":
1620-
version "5.0.1"
1621-
resolved "https://registry.yarnpkg.com/@redis/json/-/json-5.0.1.tgz#7eef09a2fac9e6ca6188ec34f9d313d4617f5b5b"
1622-
integrity sha512-t94HOTk5myfhvaHZzlUzk2hoUvH2jsjftcnMgJWuHL/pzjAJQoZDCUJzjkoXIUjWXuyJixTguaaDyOZWwqH2Kg==
1619+
"@redis/json@5.8.3":
1620+
version "5.8.3"
1621+
resolved "https://registry.yarnpkg.com/@redis/json/-/json-5.8.3.tgz#f4efab4245b9f5c1d4ab80ec6f3c13aecb663c1e"
1622+
integrity sha512-DRR09fy/u8gynHGJ4gzXYeM7D8nlS6EMv5o+h20ndTJiAc7RGR01fdk2FNjnn1Nz5PjgGGownF+s72bYG4nZKQ==
16231623

1624-
"@redis/search@5.0.1":
1625-
version "5.0.1"
1626-
resolved "https://registry.yarnpkg.com/@redis/search/-/search-5.0.1.tgz#805038a010b3d58765c65374d1a730247dfd2a5f"
1627-
integrity sha512-wipK6ZptY7K68B7YLVhP5I/wYCDUU+mDJMyJiUcQLuOs7/eKOBc8lTXKUSssor8QnzZSPy4A5ulcC5PZY22Zgw==
1624+
"@redis/search@5.8.3":
1625+
version "5.8.3"
1626+
resolved "https://registry.yarnpkg.com/@redis/search/-/search-5.8.3.tgz#eb1159d1f6576244f47d779535515e83561ec1a4"
1627+
integrity sha512-EMIvEeGRR2I0BJEz4PV88DyCuPmMT1rDtznlsHY3cKSDcc9vj0Q411jUnX0iU2vVowUgWn/cpySKjpXdZ8m+5g==
16281628

1629-
"@redis/time-series@5.0.1":
1630-
version "5.0.1"
1631-
resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-5.0.1.tgz#b63654dad199fcbcd8315c27d6ae46db9120c211"
1632-
integrity sha512-k6PgbrakhnohsEWEAdQZYt3e5vSKoIzpKvgQt8//lnWLrTZx+c3ed2sj0+pKIF4FvnSeuXLo4bBWcH0Z7Urg1A==
1629+
"@redis/time-series@5.8.3":
1630+
version "5.8.3"
1631+
resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-5.8.3.tgz#d20158dceba05fd456f2abc58b05fe16c736d18e"
1632+
integrity sha512-5Jwy3ilsUYQjzpE7WZ1lEeG1RkqQ5kHtwV1p8yxXHSEmyUbC/T/AVgyjMcm52Olj/Ov/mhDKjx6ndYUi14bXsw==
16331633

16341634
"@rtsao/scc@^1.1.0":
16351635
version "1.1.0"
@@ -7020,16 +7020,16 @@ rechoir@^0.6.2:
70207020
dependencies:
70217021
resolve "^1.1.6"
70227022

7023-
redis@^5.0.1:
7024-
version "5.0.1"
7025-
resolved "https://registry.yarnpkg.com/redis/-/redis-5.0.1.tgz#2eda8388e1350638616fa4b2dc4a9f5dbdfa1911"
7026-
integrity sha512-J8nqUjrfSq0E8NQkcHDZ4HdEQk5RMYjP3jZq02PE+ERiRxolbDNxPaTT4xh6tdrme+lJ86Goje9yMt9uzh23hQ==
7027-
dependencies:
7028-
"@redis/bloom" "5.0.1"
7029-
"@redis/client" "5.0.1"
7030-
"@redis/json" "5.0.1"
7031-
"@redis/search" "5.0.1"
7032-
"@redis/time-series" "5.0.1"
7023+
redis@^5.8.3:
7024+
version "5.8.3"
7025+
resolved "https://registry.yarnpkg.com/redis/-/redis-5.8.3.tgz#c65d52ff9099579e278bf8ce100efbadafe5580a"
7026+
integrity sha512-MfSrfV6+tEfTw8c4W0yFp6XWX8Il4laGU7Bx4kvW4uiYM1AuZ3KGqEGt1LdQHeD1nEyLpIWetZ/SpY3kkbgrYw==
7027+
dependencies:
7028+
"@redis/bloom" "5.8.3"
7029+
"@redis/client" "5.8.3"
7030+
"@redis/json" "5.8.3"
7031+
"@redis/search" "5.8.3"
7032+
"@redis/time-series" "5.8.3"
70337033

70347034
reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9:
70357035
version "1.0.10"

0 commit comments

Comments
 (0)