Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
34 changes: 34 additions & 0 deletions packages/app/src/cli/commands/app/validate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {appFlags} from '../../flags.js'
import {validateApp} from '../../services/validate.js'
import AppLinkedCommand, {AppLinkedCommandOutput} from '../../utilities/app-linked-command.js'
import {linkedAppContext} from '../../services/app-context.js'
import {globalFlags} from '@shopify/cli-kit/node/cli'

export default class Validate extends AppLinkedCommand {
static summary = 'Validate your app configuration and extensions.'

static descriptionWithMarkdown = `Validates your app's \`shopify.app.toml\` and all extension configurations against their schemas and reports any errors found.`

static description = this.descriptionWithoutMarkdown()

static flags = {
...globalFlags,
...appFlags,
}

public async run(): Promise<AppLinkedCommandOutput> {
const {flags} = await this.parse(Validate)

const {app} = await linkedAppContext({
directory: flags.path,
clientId: flags['client-id'],
forceRelink: flags.reset,
userProvidedConfigName: flags.config,
unsafeReportMode: true,
})

await validateApp(app)

return {app}
}
}
2 changes: 2 additions & 0 deletions packages/app/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import GenerateSchema from './commands/app/generate/schema.js'
import ImportExtensions from './commands/app/import-extensions.js'
import AppInfo from './commands/app/info.js'
import Init from './commands/app/init.js'
import Validate from './commands/app/validate.js'
import Release from './commands/app/release.js'
import VersionsList from './commands/app/versions/list.js'
import WebhookTrigger from './commands/app/webhook/trigger.js'
Expand Down Expand Up @@ -52,6 +53,7 @@ export const commands: {[key: string]: typeof AppLinkedCommand | typeof AppUnlin
'app:import-extensions': ImportExtensions,
'app:info': AppInfo,
'app:init': Init,
'app:validate': Validate,
'app:release': Release,
'app:config:link': ConfigLink,
'app:config:use': ConfigUse,
Expand Down
52 changes: 52 additions & 0 deletions packages/app/src/cli/services/validate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {validateApp} from './validate.js'
import {testAppLinked} from '../models/app/app.test-data.js'
import {AppErrors} from '../models/app/loader.js'
import {describe, expect, test, vi} from 'vitest'
import {renderError, renderSuccess} from '@shopify/cli-kit/node/ui'
import {AbortError} from '@shopify/cli-kit/node/error'

vi.mock('@shopify/cli-kit/node/ui')

describe('validateApp', () => {
test('renders success when there are no errors', async () => {
// Given
const app = testAppLinked()

// When
await validateApp(app)

// Then
expect(renderSuccess).toHaveBeenCalledWith({headline: 'App configuration is valid.'})
expect(renderError).not.toHaveBeenCalled()
})

test('renders errors and throws when there are validation errors', async () => {
// Given
const errors = new AppErrors()
errors.addError('/path/to/shopify.app.toml', 'client_id is required')
errors.addError('/path/to/extensions/my-ext/shopify.extension.toml', 'invalid type "unknown"')
const app = testAppLinked()
app.errors = errors

// When / Then
await expect(validateApp(app)).rejects.toThrow(AbortError)
expect(renderError).toHaveBeenCalledWith({
headline: 'Validation errors found.',
body: expect.stringContaining('client_id is required'),
})
expect(renderSuccess).not.toHaveBeenCalled()
})

test('renders success when errors object exists but is empty', async () => {
// Given
const errors = new AppErrors()
const app = testAppLinked()
app.errors = errors

// When
await validateApp(app)

// Then
expect(renderSuccess).toHaveBeenCalledWith({headline: 'App configuration is valid.'})
})
})
22 changes: 22 additions & 0 deletions packages/app/src/cli/services/validate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {AppLinkedInterface} from '../models/app/app.js'
import {stringifyMessage} from '@shopify/cli-kit/node/output'
import {renderError, renderSuccess} from '@shopify/cli-kit/node/ui'
import {AbortError} from '@shopify/cli-kit/node/error'

export async function validateApp(app: AppLinkedInterface): Promise<void> {
const errors = app.errors

if (!errors || errors.isEmpty()) {
renderSuccess({headline: 'App configuration is valid.'})
return
}

const errorMessages = errors.toJSON().map((error) => stringifyMessage(error).trim())

renderError({
headline: 'Validation errors found.',
body: errorMessages.join('\n\n'),
})

throw new AbortError('App configuration is invalid.')
}
Loading
Loading