Skip to content
Merged
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
11 changes: 11 additions & 0 deletions packages/zcli-themes/src/lib/migrate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as getVariables from './getVariables'
import * as getAssets from './getAssets'
import * as rewriteManifest from './rewriteManifest'
import * as rewriteTemplates from './rewriteTemplates'
import * as rewriteAssets from './rewriteAssets'
import * as axios from 'axios'
import { request } from '@zendesk/zcli-core'
import * as errors from '@oclif/core/lib/errors'
Expand Down Expand Up @@ -45,6 +46,7 @@ describe('migrate', () => {
const getAssetsStub = sinon.stub(getAssets, 'default')
const rewriteManifestStub = sinon.stub(rewriteManifest, 'default')
const rewriteTemplatesStub = sinon.stub(rewriteTemplates, 'default')
const rewriteAssetsStub = sinon.stub(rewriteAssets, 'default')
const requestStub = sinon.stub(request, 'requestAPI')

getManifestStub.withArgs('theme/path').returns(manifest)
Expand Down Expand Up @@ -88,6 +90,9 @@ describe('migrate', () => {
home_page: '<h1>Updated Home</h1>',
'article_pages/product_updates': '<h1>Updated Product updates</h1>',
'custom_pages/faq': '<h1>Updated FAQ</h1>'
},
assets: {
'category_tree.js': Buffer.from('console.log("tree")').toString('base64')
}
}
}) as axios.AxiosPromise
Expand Down Expand Up @@ -131,6 +136,12 @@ describe('migrate', () => {
'custom_pages/faq': '<h1>Updated FAQ</h1>'
})
).to.equal(true)

expect(
rewriteAssetsStub.calledWith('theme/path', {
'category_tree.js': Buffer.from('console.log("tree")').toString('base64')
})
).to.equal(true)
})

it('throws an error when validation fails with template errors', async () => {
Expand Down
2 changes: 2 additions & 0 deletions packages/zcli-themes/src/lib/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CliUx } from '@oclif/core'
import type { AxiosError } from 'axios'
import rewriteTemplates from './rewriteTemplates'
import rewriteManifest from './rewriteManifest'
import rewriteAssets from './rewriteAssets'
import handleTemplateError from './handleTemplateError'
import parseAxiosError from './parseAxiosError'

Expand Down Expand Up @@ -50,6 +51,7 @@ export default async function migrate (themePath: string, flags: Flags): Promise
})
rewriteManifest(themePath, data.metadata.api_version)
rewriteTemplates(themePath, data.templates)
rewriteAssets(themePath, data.assets)
Comment thread
luis-almeida marked this conversation as resolved.
CliUx.ux.action.stop('Ok')
} catch (e) {
CliUx.ux.action.stop(chalk.bold.red('!'))
Expand Down
86 changes: 86 additions & 0 deletions packages/zcli-themes/src/lib/rewriteAssets.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as sinon from 'sinon'
import * as fs from 'fs'
import { expect } from '@oclif/test'
import rewriteAssets from './rewriteAssets'

describe('rewriteAssets', () => {
beforeEach(() => {
sinon.restore()
})

it('decodes base64 content and writes assets to the correct file paths', () => {
const mkdirSyncStub = sinon.stub(fs, 'mkdirSync')
const writeFileSyncStub = sinon.stub(fs, 'writeFileSync')

const jsContent = Buffer.from('console.log("hello")').toString('base64')

rewriteAssets('theme/path', {
'script.js': jsContent
})

expect(mkdirSyncStub.calledOnce).to.equal(true)
expect(mkdirSyncStub.firstCall.args[1]).to.deep.equal({ recursive: true })

expect(writeFileSyncStub.callCount).to.equal(1)
expect(writeFileSyncStub.firstCall.args[0]).to.equal('theme/path/assets/script.js')
expect(Buffer.compare(
writeFileSyncStub.firstCall.args[1] as Buffer,
Buffer.from('console.log("hello")')
)).to.equal(0)
})

it('handles multiple file types', () => {
sinon.stub(fs, 'mkdirSync')
const writeFileSyncStub = sinon.stub(fs, 'writeFileSync')

const jsContent = Buffer.from('var a = 1;').toString('base64')
const pngContent = Buffer.from([0x89, 0x50, 0x4e, 0x47]).toString('base64')

rewriteAssets('theme/path', {
'app.js': jsContent,
'logo.png': pngContent
})

expect(writeFileSyncStub.callCount).to.equal(2)
expect(writeFileSyncStub.firstCall.args[0]).to.equal('theme/path/assets/app.js')
expect(writeFileSyncStub.secondCall.args[0]).to.equal('theme/path/assets/logo.png')

expect(Buffer.compare(
writeFileSyncStub.secondCall.args[1] as Buffer,
Buffer.from([0x89, 0x50, 0x4e, 0x47])
)).to.equal(0)
})

it('handles empty assets object', () => {
sinon.stub(fs, 'mkdirSync')
const writeFileSyncStub = sinon.stub(fs, 'writeFileSync')

rewriteAssets('theme/path', {})

expect(writeFileSyncStub.callCount).to.equal(0)
})

it('throws an error when file cannot be written', () => {
sinon.stub(fs, 'mkdirSync')
const writeFileSyncStub = sinon.stub(fs, 'writeFileSync')

writeFileSyncStub.throws(new Error('Permission denied'))

const jsContent = Buffer.from('first').toString('base64')

expect(() => {
rewriteAssets('theme/path', { 'a.js': jsContent })
}).to.throw('Failed to write asset file: theme/path/assets/a.js')
})

it('throws an error when assets directory cannot be created', () => {
sinon.stub(fs, 'mkdirSync').throws(new Error('Permission denied'))
sinon.stub(fs, 'writeFileSync')

const jsContent = Buffer.from('hello').toString('base64')

expect(() => {
rewriteAssets('theme/path', { 'a.js': jsContent })
}).to.throw('Failed to create assets directory: theme/path/assets')
})
})
23 changes: 23 additions & 0 deletions packages/zcli-themes/src/lib/rewriteAssets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { CLIError } from '@oclif/core/lib/errors'
import * as fs from 'fs'
import * as chalk from 'chalk'

export default function rewriteAssets (themePath: string, assets: Record<string, string>) {
const assetsDir = `${themePath}/assets`
Comment thread
luis-almeida marked this conversation as resolved.

try {
fs.mkdirSync(assetsDir, { recursive: true })
} catch (error) {
throw new CLIError(chalk.red(`Failed to create assets directory: ${assetsDir}`))
}

for (const [filename, base64Content] of Object.entries(assets)) {
const filePath = `${assetsDir}/${filename}`
Comment thread
luis-almeida marked this conversation as resolved.

try {
fs.writeFileSync(filePath, Buffer.from(base64Content, 'base64'))
} catch (error) {
throw new CLIError(chalk.red(`Failed to write asset file: ${filePath}`))
Comment thread
luis-almeida marked this conversation as resolved.
}
}
}
12 changes: 4 additions & 8 deletions packages/zcli-themes/src/lib/rewriteTemplates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,18 @@ describe('rewriteTemplates', () => {
])
})

it('ignores write errors', () => {
it('throws an error when file cannot be written', () => {
const writeFileSyncStub = sinon.stub(fs, 'writeFileSync')

writeFileSyncStub.onFirstCall().throws(new Error('Permission denied'))
writeFileSyncStub.onSecondCall().returns(undefined)
writeFileSyncStub.throws(new Error('Permission denied'))

const templates = {
home_page: '<h1>Updated Home</h1>',
article_page: '<h1>Updated Article</h1>'
home_page: '<h1>Updated Home</h1>'
}

expect(() => {
rewriteTemplates('theme/path', templates)
}).to.not.throw()

expect(writeFileSyncStub.callCount).to.equal(2)
}).to.throw('Failed to write template file: theme/path/templates/home_page.hbs')
})

it('handles empty templates object', () => {
Expand Down
4 changes: 3 additions & 1 deletion packages/zcli-themes/src/lib/rewriteTemplates.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { CLIError } from '@oclif/core/lib/errors'
import * as fs from 'fs'
import * as chalk from 'chalk'

export default function rewriteTemplates (themePath: string, templates: Record<string, string>) {
for (const [identifier, content] of Object.entries(templates)) {
Expand All @@ -8,7 +10,7 @@ export default function rewriteTemplates (themePath: string, templates: Record<s
try {
fs.writeFileSync(filePath, content)
} catch (error) {
// Ignore errors if file doesn't exist or can't be written
throw new CLIError(chalk.red(`Failed to write template file: ${filePath}`))
Comment thread
luis-almeida marked this conversation as resolved.
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions packages/zcli-themes/tests/functional/migrate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('themes:migrate', function () {
let fetchStub: sinon.SinonStub
let manifestBackup: string
let templateBackup: string
const migratedAssetPath = path.join(baseThemePath, 'assets/category_tree.js')

beforeEach(() => {
fetchStub = sinon.stub(global, 'fetch')
Expand All @@ -33,6 +34,10 @@ describe('themes:migrate', function () {
path.join(baseThemePath, 'templates/document_head.hbs'),
templateBackup
)
// Clean up migrated asset
if (fs.existsSync(migratedAssetPath)) {
fs.unlinkSync(migratedAssetPath)
}
})

describe('successful migration', () => {
Expand All @@ -55,6 +60,9 @@ describe('themes:migrate', function () {
},
templates: {
document_head: '{{!chat (obsolete)}}'
},
assets: {
'category_tree.js': Buffer.from('console.log("category_tree");\n').toString('base64')
}
})
)
Expand All @@ -77,6 +85,10 @@ describe('themes:migrate', function () {
'utf8'
)
expect(template).to.contain('{{!chat (obsolete)}}')

// Verify asset was written
const asset = fs.readFileSync(migratedAssetPath, 'utf8')
expect(asset).to.equal('console.log("category_tree");\n')
})
})

Expand Down
Loading