From ed3870bb606033dc57b1dca213e68f071b705240 Mon Sep 17 00:00:00 2001 From: Denys Kuchma Date: Mon, 19 Jan 2026 15:19:22 +0200 Subject: [PATCH 1/3] restore autocompletion for page objects in TypeScript --- lib/command/definitions.js | 4 ++-- test/runner/definitions_test.js | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/command/definitions.js b/lib/command/definitions.js index ade5c2535..9dea318b8 100644 --- a/lib/command/definitions.js +++ b/lib/command/definitions.js @@ -230,8 +230,8 @@ function getImportString(testsPath, targetFolderPath, pathsToType, pathsToValue) for (const name in pathsToType) { const relativePath = getPath(pathsToType[name], targetFolderPath, testsPath) - // For ESM modules with default exports, we need to access the default export type - if (relativePath.endsWith('.js')) { + // Page objects and other support modules are exported as plain objects, not as default exports + if (relativePath.endsWith('.ts')) { importStrings.push(`type ${name} = typeof import('${relativePath}')['default'];`) } else { importStrings.push(`type ${name} = typeof import('${relativePath}');`) diff --git a/test/runner/definitions_test.js b/test/runner/definitions_test.js index ea10085ba..ece2a5f7b 100644 --- a/test/runner/definitions_test.js +++ b/test/runner/definitions_test.js @@ -107,7 +107,9 @@ describe('Definitions', function () { const definitionFile = types.getSourceFileOrThrow(`${codecept_dir}/steps.d.ts`) const extend = definitionFile.getFullText() - extend.should.include("type CurrentPage = typeof import('./po/custom_steps.js')['default'];") + // Page objects are exported as plain objects, not classes + // So they should not have ['default'] to allow autocompletion + extend.should.include("type CurrentPage = typeof import('./po/custom_steps.js');") assert(!err) done() }) From 0b5a589f6a0dcf7208a87ecd5637d43aa04953ee Mon Sep 17 00:00:00 2001 From: Denys Kuchma Date: Mon, 19 Jan 2026 16:46:11 +0200 Subject: [PATCH 2/3] remove conditional logic for page object type generation --- lib/command/definitions.js | 7 +------ test/runner/definitions_test.js | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/command/definitions.js b/lib/command/definitions.js index 9dea318b8..5e720dce8 100644 --- a/lib/command/definitions.js +++ b/lib/command/definitions.js @@ -230,12 +230,7 @@ function getImportString(testsPath, targetFolderPath, pathsToType, pathsToValue) for (const name in pathsToType) { const relativePath = getPath(pathsToType[name], targetFolderPath, testsPath) - // Page objects and other support modules are exported as plain objects, not as default exports - if (relativePath.endsWith('.ts')) { - importStrings.push(`type ${name} = typeof import('${relativePath}')['default'];`) - } else { - importStrings.push(`type ${name} = typeof import('${relativePath}');`) - } + importStrings.push(`type ${name} = typeof import('${relativePath}');`) } for (const name in pathsToValue) { diff --git a/test/runner/definitions_test.js b/test/runner/definitions_test.js index ece2a5f7b..142891007 100644 --- a/test/runner/definitions_test.js +++ b/test/runner/definitions_test.js @@ -107,8 +107,8 @@ describe('Definitions', function () { const definitionFile = types.getSourceFileOrThrow(`${codecept_dir}/steps.d.ts`) const extend = definitionFile.getFullText() - // Page objects are exported as plain objects, not classes - // So they should not have ['default'] to allow autocompletion + // Page objects are exported as plain objects in .js files + // Use typeof import() without ['default'] to allow TypeScript to infer the object type extend.should.include("type CurrentPage = typeof import('./po/custom_steps.js');") assert(!err) done() From fb3ecd24f45ce105617513fe2f5da12ec65108d5 Mon Sep 17 00:00:00 2001 From: Denys Kuchma Date: Mon, 19 Jan 2026 17:22:37 +0200 Subject: [PATCH 3/3] fix logic --- lib/command/definitions.js | 11 +++++++++-- test/runner/definitions_test.js | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/command/definitions.js b/lib/command/definitions.js index 5e720dce8..95a371fa4 100644 --- a/lib/command/definitions.js +++ b/lib/command/definitions.js @@ -229,8 +229,15 @@ function getImportString(testsPath, targetFolderPath, pathsToType, pathsToValue) const importStrings = [] for (const name in pathsToType) { - const relativePath = getPath(pathsToType[name], targetFolderPath, testsPath) - importStrings.push(`type ${name} = typeof import('${relativePath}');`) + const originalPath = pathsToType[name] + const relativePath = getPath(originalPath, targetFolderPath, testsPath) + // For .js files with plain object exports, access .default to allow TypeScript to extract properties + // For .ts files, the default export is handled differently by TypeScript + if (originalPath.endsWith('.js')) { + importStrings.push(`type ${name} = typeof import('${relativePath}').default;`) + } else { + importStrings.push(`type ${name} = typeof import('${relativePath}');`) + } } for (const name in pathsToValue) { diff --git a/test/runner/definitions_test.js b/test/runner/definitions_test.js index 142891007..f5c26d364 100644 --- a/test/runner/definitions_test.js +++ b/test/runner/definitions_test.js @@ -108,8 +108,8 @@ describe('Definitions', function () { const extend = definitionFile.getFullText() // Page objects are exported as plain objects in .js files - // Use typeof import() without ['default'] to allow TypeScript to infer the object type - extend.should.include("type CurrentPage = typeof import('./po/custom_steps.js');") + // Access .default to allow TypeScript to extract properties for autocompletion + extend.should.include("type CurrentPage = typeof import('./po/custom_steps.js').default;") assert(!err) done() })