Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6ff87a3
ci: temp e2e workflow
MrMuzyk Oct 13, 2026
c5fa84d
Merge branch 'callstack:main' into main
MrMuzyk Oct 16, 2026
4feac60
feat: repack e2e tests
MrMuzyk Oct 13, 2026
e0a0ea5
Merge branch 'main' into e2e-ci
MrMuzyk Oct 19, 2026
0254419
fix: CR fixes
MrMuzyk Oct 19, 2026
da3f15e
fix: pin ruby
MrMuzyk Oct 19, 2026
53bca6d
fix: warn
MrMuzyk Oct 19, 2026
62fd861
fix: cr fixes v2
MrMuzyk Oct 20, 2026
31c9af0
feat: More features in MF2 test app
MrMuzyk Oct 23, 2026
b8b4207
Merge branch 'main' of github.com:MrMuzyk/repack into e2e-ci
MrMuzyk Oct 23, 2026
144812f
fix: lock
MrMuzyk Oct 23, 2026
e010c48
Merge branch 'e2e-ci' of github.com:MrMuzyk/repack into add-more-feat…
MrMuzyk Oct 23, 2026
46a7e94
feat: plugins
MrMuzyk Oct 26, 2026
dde9e4c
fix: prettier
MrMuzyk Oct 26, 2026
f84e401
Merge branch 'main' of github.com:MrMuzyk/repack into add-more-featur…
MrMuzyk Oct 26, 2026
4354599
fix: ids
MrMuzyk Oct 26, 2026
1612757
feat: assets tests
MrMuzyk Oct 27, 2026
e78ecbc
fix: prettier
MrMuzyk Oct 27, 2026
1ffeb98
feat: svgr
MrMuzyk Oct 28, 2026
9139910
fix: configs
MrMuzyk Oct 28, 2026
cae130c
feat: design doc and some cleanup of duplicated tests
MrMuzyk Oct 28, 2026
5bb638f
fix: final touches
MrMuzyk Oct 29, 2026
d2074ff
fix: timeout as android is slower
MrMuzyk Oct 29, 2026
351a1ef
fix: reduce motion warning
MrMuzyk Oct 29, 2026
746a7d5
fix: failing android run
MrMuzyk Oct 30, 2026
66a9047
fix: added some missing debugging logs and fixed some minor stuff
MrMuzyk Oct 30, 2026
fff3824
Merge branch 'main' into add-more-features-to-mf2-testing-app
MrMuzyk Nov 4, 2026
a28beee
Merge branch 'main' into add-more-features-to-mf2-testing-app
MrMuzyk Nov 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ jobs:
arch: x86_64
profile: pixel_7
avd-name: e2e
disable-animations: false
working-directory: apps/tester-federation-v2
script: |
adb shell settings put global hide_error_dialogs 1
Expand Down
1 change: 1 addition & 0 deletions agent_context/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ works, and is kept in sync with the implementation as it evolves.
| Folder | Topic |
| --- | --- |
| [rspackv2-jul2026](./rspackv2-jul2026/design.md) | Dual Rspack 1.x/2.x support |
| [e2e-oct2026](./e2e-oct2026/design.md) | e2e tests on an emulator, on CI |
254 changes: 254 additions & 0 deletions agent_context/e2e-oct2026/design.md

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions apps/tester-federation-v2/babel.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: ['react-native-worklets/plugin'],
comments: true,
overrides: [
{
// Regex instead of a relative string: babel resolves strings against the
// launch cwd, and the rspack CLI launches from the repo root.
test: /[\\/]src[\\/]host[\\/]/,
plugins: [
[
'@babel/plugin-transform-react-jsx',
{ runtime: 'automatic', importSource: 'nativewind' },
],
],
},
],
};
44 changes: 44 additions & 0 deletions apps/tester-federation-v2/configs/hostAssetRules.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import path from 'node:path';
import * as Repack from '@callstack/repack';

const BITMAP_EXTENSIONS = Repack.getAssetExtensionsRegExp(
Repack.ASSET_EXTENSIONS.filter((ext) => ext !== 'svg')
);

export function buildHostAssetRules(context: string) {
const localAssets = path.join(context, 'src/host/assets/localAssets');
const inlineAssets = path.join(context, 'src/host/assets/inlineAssets');

return [
{
test: BITMAP_EXTENSIONS,
exclude: [localAssets, inlineAssets],
use: {
loader: '@callstack/repack/assets-loader',
options: { inline: true, maxInlineSize: 90 * 1024 },
},
},
{
test: /\.svg$/,
use: [
{
loader: '@svgr/webpack',
options: { native: true, dimensions: false },
},
],
},
{
test: BITMAP_EXTENSIONS,
include: [localAssets],
use: '@callstack/repack/assets-loader',
},
{
test: BITMAP_EXTENSIONS,
include: [inlineAssets],
use: {
loader: '@callstack/repack/assets-loader',
options: { inline: true },
},
},
];
}
16 changes: 12 additions & 4 deletions apps/tester-federation-v2/configs/rspack.host-app.mts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import * as Repack from '@callstack/repack';
import { NativeWindPlugin } from '@callstack/repack-plugin-nativewind';
import { ReanimatedPlugin } from '@callstack/repack-plugin-reanimated';
import rspack from '@rspack/core';

import { buildHostAssetRules } from './hostAssetRules.mts';
import { buildSharedSingletons } from './sharedSingletons.mts';

export default Repack.defineRspackConfig((env) => {
Expand Down Expand Up @@ -28,15 +31,19 @@ export default Repack.defineRspackConfig((env) => {
},
type: 'javascript/auto',
},
...Repack.getAssetTransformRules({
inline: true,
maxInlineSize: 90 * 1024,
}),
...buildHostAssetRules(context!),
],
},
plugins: [
new NativeWindPlugin({
cssInteropOptions: { inlineRem: 16 },
}),
new Repack.RepackPlugin({
extraChunks: [
{
include: /^host-async-local$/,
type: 'local',
},
{
include: /.*/,
type: 'remote',
Expand All @@ -62,6 +69,7 @@ export default Repack.defineRspackConfig((env) => {
process.env.WITH_PRELOAD === 'true' ||
process.env.WITH_PRELOAD === '1',
}),
new ReanimatedPlugin({ unstable_disableTransform: true }),
],
};
});
15 changes: 12 additions & 3 deletions apps/tester-federation-v2/configs/webpack.host-app.mts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// @ts-check
import * as Repack from '@callstack/repack';
import { NativeWindPlugin } from '@callstack/repack-plugin-nativewind';
import { ReanimatedPlugin } from '@callstack/repack-plugin-reanimated';
import webpack from 'webpack';

import { buildHostAssetRules } from './hostAssetRules.mts';
import { buildSharedSingletons } from './sharedSingletons.mts';

export default Repack.defineWebpackConfig((env) => {
Expand All @@ -25,21 +28,26 @@ export default Repack.defineWebpackConfig((env) => {
use: '@callstack/repack/babel-swc-loader',
type: 'javascript/auto',
},
...Repack.getAssetTransformRules(),
...buildHostAssetRules(context!),
],
},
plugins: [
// @ts-expect-error
new NativeWindPlugin({
cssInteropOptions: { inlineRem: 16 },
}),
new Repack.RepackPlugin({
extraChunks: [
{
include: /^host-async-local$/,
type: 'local',
},
{
include: /.*/,
type: 'remote',
outputPath: `build/host-app/${platform}/output-remote`,
},
],
}),
// @ts-expect-error
new Repack.plugins.ModuleFederationPluginV2({
name: 'HostApp',
filename: 'HostApp.container.js.bundle',
Expand All @@ -58,6 +66,7 @@ export default Repack.defineWebpackConfig((env) => {
process.env.WITH_PRELOAD === 'true' ||
process.env.WITH_PRELOAD === '1',
}),
new ReanimatedPlugin({ unstable_disableTransform: true }),
],
};
});
2 changes: 0 additions & 2 deletions apps/tester-federation-v2/configs/webpack.mini-app.mts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export default Repack.defineWebpackConfig((env) => {
],
},
plugins: [
// @ts-expect-error
new Repack.RepackPlugin({
extraChunks: [
{
Expand All @@ -38,7 +37,6 @@ export default Repack.defineWebpackConfig((env) => {
},
],
}),
// @ts-expect-error
new Repack.plugins.ModuleFederationPluginV2({
name: 'MiniApp',
filename: 'MiniApp.container.js.bundle',
Expand Down
19 changes: 19 additions & 0 deletions apps/tester-federation-v2/e2e/async-chunk.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { test } from '@e2e-dev/mobile';
import { expect } from 'e2e';

test('Async chunk loads through ScriptManager and renders', async ({
app,
screen,
}) => {
await app.open();
await expect(screen.getByTestId('home-screen')).toBeVisible({
timeout: 30_000,
});

const entry = screen.getByTestId('home-async-chunk-button');
await screen.scrollUntilVisible(entry);
await entry.tap();
await expect(screen.getByTestId('async-chunk-screen')).toBeVisible();

await expect(screen.getByTestId('async-chunk-content')).toBeVisible();
});
21 changes: 21 additions & 0 deletions apps/tester-federation-v2/e2e/gallery.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { test } from '@e2e-dev/mobile';
import { expect } from 'e2e';

test('Gallery MiniApp loads and renders its own navigator', async ({
app,
screen,
}) => {
await app.open();
await expect(screen.getByTestId('home-screen')).toBeVisible({
timeout: 30_000,
});

const entry = screen.getByTestId('home-gallery-button');
await screen.scrollUntilVisible(entry);
await entry.tap();

await expect(screen.getByTestId('miniapp-gallery')).toBeVisible({
timeout: 45_000,
});
await expect(screen.getByText('Picture 1')).toBeVisible();
});
44 changes: 44 additions & 0 deletions apps/tester-federation-v2/e2e/host-assets.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { test } from '@e2e-dev/mobile';
import { expect } from 'e2e';

test('Host Assets screen renders', async ({ app, screen }) => {
await app.open();
await expect(screen.getByTestId('home-screen')).toBeVisible({
timeout: 30_000,
});

const entry = screen.getByTestId('home-host-assets-button');
await screen.scrollUntilVisible(entry);
await entry.tap();
await expect(screen.getByTestId('host-assets-screen')).toBeVisible();

const pic1Mode = screen.getByTestId('host-asset-pic-1-mode-report');
const pic2Mode = screen.getByTestId('host-asset-pic-2-mode-report');
const localMode = screen.getByTestId('host-asset-local-mode-report');
const inlineMode = screen.getByTestId('host-asset-inline-mode-report');
const localUri = screen.getByTestId('host-asset-local-uri-report');

await screen.scrollUntilVisible(pic1Mode);
await expect(pic1Mode).toHaveText('mode: Inlined');
await screen.scrollUntilVisible(pic2Mode);
await expect(pic2Mode).toHaveText('mode: Emitted');
await screen.scrollUntilVisible(localMode);
await expect(localMode).toHaveText('mode: Emitted');
await screen.scrollUntilVisible(inlineMode);
await expect(inlineMode).toHaveText('mode: Inlined');

await screen.scrollUntilVisible(localUri);
const uriText = (await localUri.textContent()) ?? '';
const assetUrl = new URL(uriText.slice('uri: '.length));
expect(assetUrl.protocol).toBe('http:');
expect(assetUrl.port).toBe('8081');
// Android resolves the dev server as 10.0.2.2 and iOS as localhost
expect(['localhost', '10.0.2.2']).toContain(assetUrl.hostname);

const probeHost =
assetUrl.hostname === '10.0.2.2' ? 'localhost' : assetUrl.hostname;
const response = await fetch(
`${assetUrl.protocol}//${probeHost}:${assetUrl.port}${assetUrl.pathname}${assetUrl.search}`
);
expect(response.status).toBe(200);
});
34 changes: 34 additions & 0 deletions apps/tester-federation-v2/e2e/miniapp-install.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { test } from '@e2e-dev/mobile';
import { expect } from 'e2e';

test('install, show, and remove the host mini-app chunk', async ({
app,
screen,
}) => {
await app.open();
await expect(screen.getByTestId('home-screen')).toBeVisible({
timeout: 30_000,
});

const entry = screen.getByTestId('home-miniapp-install-button');
await screen.scrollUntilVisible(entry);
await entry.tap();
await expect(screen.getByTestId('miniapp-install-screen')).toBeVisible();

await screen.getByTestId('miniapp-install-button').tap();
await expect(screen.getByTestId('miniapp-install-button')).toBeDisabled();

await screen.getByTestId('miniapp-toggle-button').tap();
await expect(screen.getByTestId('miniapp-install-content')).toBeVisible();

await screen.getByTestId('miniapp-toggle-button').tap();
await expect(screen.getByTestId('miniapp-install-content')).toBeHidden();

await screen.getByTestId('miniapp-toggle-button').tap();
await expect(screen.getByTestId('miniapp-install-content')).toBeVisible();

await screen.getByTestId('miniapp-uninstall-button').tap();
await expect(screen.getByTestId('miniapp-install-content')).toBeHidden();
await expect(screen.getByTestId('miniapp-install-button')).toBeEnabled();
await expect(screen.getByTestId('miniapp-toggle-button')).toBeDisabled();
});
30 changes: 30 additions & 0 deletions apps/tester-federation-v2/e2e/nativewind.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { test } from '@e2e-dev/mobile';
import { expect } from 'e2e';

test('NativeWind styles render, respond and switch to dark', async ({
app,
screen,
}) => {
await app.open();
await expect(screen.getByTestId('home-screen')).toBeVisible({
timeout: 30_000,
});

const entry = screen.getByTestId('home-nativewind-button');
await screen.scrollUntilVisible(entry);
await entry.tap();
await expect(screen.getByTestId('nativewind-screen')).toBeVisible();

const padding = screen.getByTestId('nativewind-padding-report');
await expect(padding).toHaveText('padding-applied: 16');

const calcWidth = screen.getByTestId('nativewind-calc-width-report');
await expect(calcWidth).toHaveText('calc-width: 248');

const colorScheme = screen.getByTestId('nativewind-color-scheme-report');
await expect(colorScheme).toHaveText('color-scheme: light');
await screen.getByTestId('nativewind-dark-mode-button').tap();
await expect(colorScheme).toHaveText('color-scheme: dark');
await screen.getByTestId('nativewind-dark-mode-button').tap();
await expect(colorScheme).toHaveText('color-scheme: light');
});
26 changes: 26 additions & 0 deletions apps/tester-federation-v2/e2e/reanimated.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { test } from '@e2e-dev/mobile';
import { expect } from 'e2e';

test('Reanimated spring tracks taps and reset', async ({ app, screen }) => {
await app.open();
await expect(screen.getByTestId('home-screen')).toBeVisible({
timeout: 30_000,
});

const entry = screen.getByTestId('home-reanimated-button');
await screen.scrollUntilVisible(entry);
await entry.tap();
await expect(screen.getByTestId('reanimated-screen')).toBeVisible();

const width = screen.getByTestId('reanimated-width-report');
await expect(width).toHaveText('width: 100');

await screen.getByTestId('reanimated-click-button').tap();
await expect(width).toHaveText('width: 150');

await screen.getByTestId('reanimated-click-button').tap();
await expect(width).toHaveText('width: 200');

await screen.getByTestId('reanimated-reset-button').tap();
await expect(width).toHaveText('width: 100');
});
32 changes: 32 additions & 0 deletions apps/tester-federation-v2/e2e/remote-chunk.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { test } from '@e2e-dev/mobile';
import { expect } from 'e2e';

test('prefetch, load, and invalidate the host remote chunk', async ({
app,
screen,
}) => {
await app.open();
await expect(screen.getByTestId('home-screen')).toBeVisible({
timeout: 30_000,
});

const entry = screen.getByTestId('home-remote-chunk-button');
await screen.scrollUntilVisible(entry);
await entry.tap();
await expect(screen.getByTestId('remote-chunk-screen')).toBeVisible();

await screen.getByTestId('remote-chunk-prefetch-button').tap();
await expect(
screen.getByTestId('remote-chunk-prefetch-button')
).toBeDisabled();

await screen.getByTestId('remote-chunk-load-button').tap();
await expect(screen.getByTestId('remote-chunk-content')).toBeVisible();

await screen.getByTestId('remote-chunk-invalidate-button').tap();
await expect(screen.getByTestId('remote-chunk-content')).toBeHidden();
await expect(screen.getByTestId('remote-chunk-load-button')).toBeVisible();
await expect(
screen.getByTestId('remote-chunk-prefetch-button')
).toBeEnabled();
});
Loading