From 7b4f7d6f66ebceee20a688f37a7b973390235615 Mon Sep 17 00:00:00 2001 From: Arjun Gupta Date: Mon, 13 Jul 2026 16:39:23 +0530 Subject: [PATCH 1/2] Fix #817: Using cli outside of projects should produce a helpful error message Generated by Prism (AI Week 2026) --- src/hooks/command_error.js | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/hooks/command_error.js diff --git a/src/hooks/command_error.js b/src/hooks/command_error.js new file mode 100644 index 0000000..74072d1 --- /dev/null +++ b/src/hooks/command_error.js @@ -0,0 +1,55 @@ +/* + * Copyright 2024 Adobe Inc. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +const fs = require('fs') +const path = require('path') +const chalk = require('chalk') + +/** + * Hook that runs when a command error occurs. + * Provides helpful error messages when authentication/project errors occur. + * + * @param {object} options the hook options + * @param {string} options.message the error message + */ +async function hook (options) { + const { message } = options + + // Check if the error is authentication-related + const authErrors = [ + 'An AUTH key must be specified', + 'AUTH key must be specified', + 'authentication', + 'not authenticated' + ] + + const isAuthError = authErrors.some(error => + message && message.toLowerCase().includes(error.toLowerCase()) + ) + + if (isAuthError) { + // Check if we're in an App Builder project directory + const appConfigPath = path.join(process.cwd(), 'app.config.yaml') + const hasAppConfig = fs.existsSync(appConfigPath) + + if (!hasAppConfig) { + // Provide a helpful error message + console.error(chalk.red('\n❌ You are not in an App Builder project directory.\n')) + console.error(chalk.yellow('To fix this:')) + console.error(chalk.yellow(' 1. Navigate to an existing App Builder project directory, or')) + console.error(chalk.yellow(' 2. Create a new App Builder project with: ') + chalk.cyan('aio app init')) + console.error(chalk.yellow('\nFor more information, visit: https://developer.adobe.com/app-builder/docs/\n')) + } + } +} + +module.exports = hook From 35d94f76ff8e65ee5dc0ac3cc8b62b6a278718c2 Mon Sep 17 00:00:00 2001 From: Arjun Gupta Date: Wed, 15 Jul 2026 15:19:17 +0530 Subject: [PATCH 2/2] Prism revision (attempt 1): address PR feedback Perfect! I've created a comprehensive test file for the `command_error.js` hook that should achieve 100% coverage. The test file: 1. **Tests all authentication error patterns**: "An AUTH key must be specified", "AUTH key must be specified", "authentication", and "not authenticated" 2. **Tests both scenarios**: inside and outside an App Builder project (using `fs.existsSync` mock) 3. **Tests case-insensitive matching**: Ensures the error detection works regardless of case 4. **Tests edge cases**: undefined, null, and empty messages 5. **Tests non-auth errors**: Ensures the hook doesn't interfere with other error types This should bring the coverage back to 100% for all metrics (statements, branches, functions, and lines) and make the CI build pass. --- test/hooks/command_error.test.js | 114 +++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 test/hooks/command_error.test.js diff --git a/test/hooks/command_error.test.js b/test/hooks/command_error.test.js new file mode 100644 index 0000000..d37fad2 --- /dev/null +++ b/test/hooks/command_error.test.js @@ -0,0 +1,114 @@ +/* + * Copyright 2024 Adobe Inc. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +const hook = require('../../src/hooks/command_error') +const fs = require('fs') +const path = require('path') + +jest.mock('fs') + +describe('command_error hook', () => { + let consoleErrorSpy + + beforeEach(() => { + consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation() + jest.clearAllMocks() + }) + + afterEach(() => { + consoleErrorSpy.mockRestore() + }) + + describe('authentication errors', () => { + test('shows helpful message when auth error occurs outside App Builder project', async () => { + fs.existsSync.mockReturnValue(false) + + await hook({ message: 'An AUTH key must be specified' }) + + expect(fs.existsSync).toHaveBeenCalledWith(path.join(process.cwd(), 'app.config.yaml')) + expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('You are not in an App Builder project directory')) + expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('To fix this:')) + expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('aio app init')) + }) + + test('shows helpful message for "AUTH key must be specified" error', async () => { + fs.existsSync.mockReturnValue(false) + + await hook({ message: 'AUTH key must be specified' }) + + expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('You are not in an App Builder project directory')) + }) + + test('shows helpful message for "authentication" error', async () => { + fs.existsSync.mockReturnValue(false) + + await hook({ message: 'authentication failed' }) + + expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('You are not in an App Builder project directory')) + }) + + test('shows helpful message for "not authenticated" error', async () => { + fs.existsSync.mockReturnValue(false) + + await hook({ message: 'not authenticated' }) + + expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('You are not in an App Builder project directory')) + }) + + test('does not show message when auth error occurs inside App Builder project', async () => { + fs.existsSync.mockReturnValue(true) + + await hook({ message: 'An AUTH key must be specified' }) + + expect(fs.existsSync).toHaveBeenCalledWith(path.join(process.cwd(), 'app.config.yaml')) + expect(consoleErrorSpy).not.toHaveBeenCalled() + }) + + test('handles case-insensitive auth error matching', async () => { + fs.existsSync.mockReturnValue(false) + + await hook({ message: 'an auth KEY must be SPECIFIED' }) + + expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('You are not in an App Builder project directory')) + }) + }) + + describe('non-authentication errors', () => { + test('does not show message for non-auth errors', async () => { + await hook({ message: 'Some other error occurred' }) + + expect(fs.existsSync).not.toHaveBeenCalled() + expect(consoleErrorSpy).not.toHaveBeenCalled() + }) + + test('handles undefined message', async () => { + await hook({ message: undefined }) + + expect(fs.existsSync).not.toHaveBeenCalled() + expect(consoleErrorSpy).not.toHaveBeenCalled() + }) + + test('handles null message', async () => { + await hook({ message: null }) + + expect(fs.existsSync).not.toHaveBeenCalled() + expect(consoleErrorSpy).not.toHaveBeenCalled() + }) + + test('handles empty message', async () => { + await hook({ message: '' }) + + expect(fs.existsSync).not.toHaveBeenCalled() + expect(consoleErrorSpy).not.toHaveBeenCalled() + }) + }) +})