diff --git a/packages/engine-rn-next/src/sdk/__tests__/env.test.ts b/packages/engine-rn-next/src/sdk/__tests__/env.test.ts new file mode 100644 index 000000000..375c742c1 --- /dev/null +++ b/packages/engine-rn-next/src/sdk/__tests__/env.test.ts @@ -0,0 +1,160 @@ +import { createRnvContext, fsExistsSync, getConfigProp, getContext, logWarning, parsePlugins } from '@rnv/core'; +import { EnvVars } from '../env'; +import { getExportDir } from '../runner'; + +jest.mock('@rnv/core'); +jest.mock('path'); +jest.mock('../runner'); + +describe('EnvVars', () => { + beforeEach(() => { + createRnvContext(); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + describe('RNV_NEXT_TRANSPILE_MODULES', () => { + it('should return an empty array when no modules are configured', () => { + // GIVEN + jest.mocked(getConfigProp).mockReturnValueOnce(undefined); + //WHEN + const result = EnvVars.RNV_NEXT_TRANSPILE_MODULES(); + //THEN + expect(result.RNV_NEXT_TRANSPILE_MODULES).toEqual([]); + }); + + it('should return configured modules', () => { + // GIVEN + + const modules = ['module1', 'module2']; + jest.mocked(getConfigProp).mockReturnValueOnce(modules); + //WHEN + const result = EnvVars.RNV_NEXT_TRANSPILE_MODULES(); + //THEN + expect(result.RNV_NEXT_TRANSPILE_MODULES).toEqual(modules); + }); + + it('should return modules from plugins', () => { + // GIVEN + const ctx = getContext(); + const plugins = { + plugin1: { webpackConfig: { nextTranspileModules: ['module3'] } }, + }; + ctx.paths.project.dir = '/path/to/project'; + ctx.platform = 'web'; + ctx.buildConfig.plugins = plugins; + + jest.mocked(getConfigProp).mockReturnValueOnce(undefined); + jest.mocked(parsePlugins).mockImplementationOnce((cb) => { + Object.keys(plugins).forEach((key) => { + cb(plugins[key], {}, key); + }); + }); + //WHEN + const result = EnvVars.RNV_NEXT_TRANSPILE_MODULES(); + //THEN + expect(result.RNV_NEXT_TRANSPILE_MODULES).toEqual(['plugin1', 'module3']); + }); + }); + + describe('NEXT_BASE', () => { + it('should return NEXT_BASE variables', () => { + //GIVEN + const ctx = getContext(); + ctx._currentTask = 'export'; + const pagesDir = 'custom/pages'; + jest.mocked(getConfigProp).mockReturnValueOnce(pagesDir); + jest.mocked(fsExistsSync).mockReturnValueOnce(true); + jest.mocked(getExportDir).mockReturnValue('/path/to/export'); + //WHEN + const result = EnvVars.NEXT_BASE(); + //THEN + expect(result).toEqual({ + NEXT_PAGES_DIR: pagesDir, + NEXT_DIST_DIR: '/path/to/export', + NEXT_EXPORT: true, + }); + }); + + it('should log warning if custom pagesDir does not exist', () => { + //GIVEN + const ctx = getContext(); + ctx._currentTask = 'export'; + const pagesDir = 'custom/pages'; + jest.mocked(getConfigProp).mockReturnValueOnce(pagesDir); + jest.mocked(fsExistsSync).mockReturnValueOnce(false); + jest.mocked(getExportDir).mockReturnValue('/path/to/export'); + //WHEN + const result = EnvVars.NEXT_BASE(); + //THEN + expect(logWarning).toHaveBeenCalledWith(expect.stringContaining('missing at')); + expect(result).toEqual({ + NEXT_PAGES_DIR: pagesDir, + NEXT_DIST_DIR: '/path/to/export', + NEXT_EXPORT: true, + }); + }); + + it('should log warning if pagesDir is not configured and use fallback', () => { + //GIVEN + const ctx = getContext(); + ctx.platform = 'web'; + ctx._currentTask = 'export'; + jest.mocked(getConfigProp).mockReturnValueOnce(undefined); + jest.mocked(fsExistsSync).mockReturnValueOnce(true); + jest.mocked(getExportDir).mockReturnValue('/path/to/export'); + //WHEN + const result = EnvVars.NEXT_BASE(); + //THEN + expect(logWarning).toHaveBeenCalledWith( + expect.stringContaining("You're missing web.pagesDir config. Defaulting to 'src/app'") + ); + expect(result).toEqual({ + NEXT_PAGES_DIR: 'src/app', + NEXT_DIST_DIR: '/path/to/export', + NEXT_EXPORT: true, + }); + }); + + it('should log warning if fallback pagesDir does not exist', () => { + //GIVEN + const ctx = getContext(); + ctx.platform = 'web'; + ctx._currentTask = 'export'; + jest.mocked(getConfigProp).mockReturnValueOnce(undefined); + jest.mocked(fsExistsSync).mockReturnValueOnce(false); + jest.mocked(getExportDir).mockReturnValue('/path/to/export'); + //WHEN + const result = EnvVars.NEXT_BASE(); + //THEN + expect(logWarning).toHaveBeenCalledWith(expect.stringContaining('Folder src/app is missing')); + expect(result).toEqual({ + NEXT_PAGES_DIR: 'src/app', + NEXT_DIST_DIR: '/path/to/export', + NEXT_EXPORT: true, + }); + }); + }); + + describe('NODE_ENV', () => { + it('should return NODE_ENV from config', () => { + //GIVEN + const env = 'production'; + jest.mocked(getConfigProp).mockReturnValueOnce(env); + //WHEN + const result = EnvVars.NODE_ENV(); + //THEN + expect(result.NODE_ENV).toEqual(env); + }); + + it('should return development if NODE_ENV is not configured', () => { + //GIVEN + jest.mocked(getConfigProp).mockReturnValueOnce(undefined); + //WHEN + const result = EnvVars.NODE_ENV(); + //THEN + expect(result.NODE_ENV).toEqual('development'); + }); + }); +}); diff --git a/packages/engine-rn-next/src/sdk/__tests__/runner.test.ts b/packages/engine-rn-next/src/sdk/__tests__/runner.test.ts new file mode 100644 index 000000000..f23feffcd --- /dev/null +++ b/packages/engine-rn-next/src/sdk/__tests__/runner.test.ts @@ -0,0 +1,145 @@ +import path from 'path'; +import { buildWebNext, exportWebNext, runWebNext } from '../runner'; +import { checkPortInUse, confirmActiveBundler, getDevServerHost, waitForHost } from '@rnv/sdk-utils'; +import { + getContext, + getConfigProp, + logDefault, + logInfo, + logSummary, + createRnvContext, + logRaw, + executeAsync, + logSuccess, + chalk, +} from '@rnv/core'; + +jest.mock('../env'); +jest.mock('@rnv/core'); +jest.mock('path'); + +jest.mock('chalk', () => ({ + bold: { + white: jest.fn((str) => str), + }, + cyan: jest.fn((str) => str), +})); + +beforeEach(() => { + createRnvContext(); + jest.mocked(getDevServerHost).mockReturnValue('localhost'); + jest.mocked(path.join).mockReturnValue('/path/to/next'); +}); + +afterEach(() => { + jest.resetAllMocks(); +}); + +describe('runWebNext', () => { + it('should return if platform is not defined', async () => { + //GIVEN + const ctx = getContext(); + ctx.runtime.port = 3000; + //WHEN + await runWebNext(); + //THEN + expect(logDefault).toHaveBeenCalledWith('runWebNext', 'port:3000'); + expect(checkPortInUse).not.toHaveBeenCalled(); + }); + it('should start dev server if port is not in use', async () => { + //GIVEN + const ctx = getContext(); + ctx.platform = 'web'; + ctx.runtime.port = 3000; + ctx.runtime.shouldOpenBrowser = true; + ctx.paths.project.dir = '/path/to/project'; + jest.mocked(checkPortInUse).mockResolvedValue(false); + jest.mocked(getConfigProp).mockReturnValue(false); + jest.mocked(waitForHost).mockResolvedValue(undefined); + + //WHEN + await runWebNext(); + + //THEN + expect(logInfo).toHaveBeenCalledWith( + 'Your web devServerHost localhost at port 3000 is not running. Starting it up for you...' + ); + expect(logSummary).toHaveBeenCalledWith({ header: 'BUNDLER STARTED' }); + expect(logRaw).toHaveBeenCalledWith(expect.stringContaining('Dev server running at: http://localhost:3000')); + expect(executeAsync).toHaveBeenCalledWith( + expect.stringContaining('node "/path/to/next" dev --port 3000'), + expect.any(Object) + ); + }); + it('should handle active bundler when port is in use and reset is completed', async () => { + //GIVEN + const ctx = getContext(); + ctx.platform = 'web'; + ctx.runtime.port = 3000; + ctx.runtime.shouldOpenBrowser = true; + ctx.paths.project.dir = '/path/to/project'; + jest.mocked(checkPortInUse).mockResolvedValue(true); + jest.mocked(confirmActiveBundler).mockResolvedValue(true); + jest.mocked(getConfigProp).mockReturnValue(false); + jest.mocked(waitForHost).mockResolvedValue(undefined); + + //WHEN + await runWebNext(); + + //THEN + expect(confirmActiveBundler).toHaveBeenCalled(); + expect(logSummary).toHaveBeenCalledWith({ header: 'BUNDLER STARTED' }); + expect(logRaw).toHaveBeenCalledWith(` +Dev server running at: ${chalk().cyan(`http://localhost:${ctx.runtime.port}`)} +`); + expect(executeAsync).toHaveBeenCalledWith( + expect.stringContaining('node "/path/to/next" dev --port 3000'), + expect.any(Object) + ); + }); +}); + +describe('buildWebNext', () => { + it('should build the project and log success message', async () => { + // GIVEN + const ctx = getContext(); + ctx.paths.project.dir = '/path/to/project'; + const buildLocation = `${ctx.paths.project.dir}/output`; + jest.mocked(getConfigProp).mockReturnValue(buildLocation); + jest.mocked(path.isAbsolute).mockReturnValue(false); + // WHEN + await expect(buildWebNext()).resolves.toEqual(true); + + // THEN + expect(logDefault).toHaveBeenCalledWith('buildWebNext'); + expect(executeAsync).toHaveBeenCalledWith( + expect.stringContaining('node /path/to/next build'), + expect.any(Object) + ); + expect(logSuccess).toHaveBeenCalledWith(`Your build is located in ${chalk().cyan(`${buildLocation}`)} .`); + }); +}); + +describe('exportWebNext', () => { + it('should export the project and log success message', async () => { + // GIVEN + const ctx = getContext(); + ctx.paths.project.dir = '/path/to/project'; + const buildLocation = `${ctx.paths.project.dir}/output`; + jest.mocked(getConfigProp).mockReturnValue(buildLocation); + jest.mocked(path.isAbsolute).mockReturnValue(false); + // WHEN + await exportWebNext(); + // THEN + expect(logDefault).toHaveBeenCalledWith('exportWebNext'); + expect(executeAsync).toHaveBeenCalledWith( + expect.stringContaining('node /path/to/next build'), + expect.objectContaining({ + env: expect.objectContaining({ + NODE_ENV: 'production', + }), + }) + ); + expect(logSuccess).toHaveBeenCalledWith(`Your export is located in ${chalk().cyan(`${buildLocation}`)} .`); + }); +}); diff --git a/packages/engine-rn-next/src/tasks/__tests__/taskRun.test.ts b/packages/engine-rn-next/src/tasks/__tests__/taskRun.test.ts new file mode 100644 index 000000000..d1e61878e --- /dev/null +++ b/packages/engine-rn-next/src/tasks/__tests__/taskRun.test.ts @@ -0,0 +1,33 @@ +import { createRnvContext, getContext } from '@rnv/core'; +import taskRun from '../taskRun'; +import { runWebNext } from '../../sdk/runner'; + +jest.mock('@rnv/core'); +jest.mock('../../sdk/runner'); + +beforeEach(() => { + createRnvContext(); +}); + +afterEach(() => { + jest.resetAllMocks(); +}); + +describe('taskRun tests', () => { + it('Execute task.rnv.run -p web', async () => { + //GIVEN + const ctx = getContext(); + //WHEN + await expect( + taskRun.fn?.({ + ctx, + taskName: 'MOCK_taskName', + originTaskName: 'MOCK_originTaskName', + parentTaskName: 'MOCK_parentTaskName', + shouldSkip: false, + }) + ).resolves.toEqual(undefined); + //THEN + expect(runWebNext).toHaveBeenCalled(); + }); +});