Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added the 'spo hubsite theme sync' command solving #401
- Loading branch information
1 parent
9e627b5
commit 6997478
Showing
5 changed files
with
455 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# spo hubsite theme sync | ||
|
||
Applies any theme updates from the parent hub site. | ||
|
||
!!! attention | ||
This command is based on a SharePoint API that is currently in preview and is subject to change once the API reached general availability. | ||
|
||
## Usage | ||
|
||
```sh | ||
spo hubsite theme sync [options] | ||
``` | ||
|
||
## Options | ||
|
||
Option|Description | ||
------|----------- | ||
`--help`|output usage information | ||
`-u, --webUrl <webUrl>`|URL of the site to apply theme updates from the hub site to. | ||
`-o, --output [output]`|Output type. `json|text`. Default `text` | ||
`--verbose`|Runs command with verbose logging | ||
`--debug`|Runs command with debug logging | ||
|
||
!!! important | ||
Before using this command, connect to a SharePoint Online site, using the [spo connect](../connect.md) command. | ||
|
||
## Remarks | ||
|
||
To apply hub site theme updates to a site, you have to first connect to a SharePoint site using the [spo connect](../connect.md) command, eg. `spo connect https://contoso.sharepoint.com`. | ||
|
||
## Examples | ||
|
||
Applies any theme updates from the parent hub site to the site with URL https://contoso.sharepoint.com/sites/project-x | ||
|
||
```sh | ||
spo hubsite theme sync --webUrl https://contoso.sharepoint.com/sites/project-x | ||
``` | ||
|
||
## More information | ||
|
||
- SharePoint hub sites new in Office 365: [https://techcommunity.microsoft.com/t5/SharePoint-Blog/SharePoint-hub-sites-new-in-Office-365/ba-p/109547](https://techcommunity.microsoft.com/t5/SharePoint-Blog/SharePoint-hub-sites-new-in-Office-365/ba-p/109547) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
273 changes: 273 additions & 0 deletions
273
src/o365/spo/commands/hubsite/hubsite-theme-sync.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
import commands from '../../commands'; | ||
import Command, { CommandOption, CommandValidate, CommandError } from '../../../../Command'; | ||
import * as sinon from 'sinon'; | ||
import appInsights from '../../../../appInsights'; | ||
import auth, { Site } from '../../SpoAuth'; | ||
const command: Command = require('./hubsite-theme-sync'); | ||
import * as assert from 'assert'; | ||
import * as request from 'request-promise-native'; | ||
import Utils from '../../../../Utils'; | ||
|
||
describe(commands.HUBSITE_THEME_SYNC, () => { | ||
let vorpal: Vorpal; | ||
let log: string[]; | ||
let cmdInstance: any; | ||
let cmdInstanceLogSpy: sinon.SinonSpy; | ||
let trackEvent: any; | ||
let telemetry: any; | ||
|
||
before(() => { | ||
sinon.stub(auth, 'restoreAuth').callsFake(() => Promise.resolve()); | ||
sinon.stub(auth, 'getAccessToken').callsFake(() => { return Promise.resolve('ABC'); }); | ||
sinon.stub(command as any, 'getRequestDigestForSite').callsFake(() => Promise.resolve({ FormDigestValue: 'ABC' })); | ||
trackEvent = sinon.stub(appInsights, 'trackEvent').callsFake((t) => { | ||
telemetry = t; | ||
}); | ||
}); | ||
|
||
beforeEach(() => { | ||
vorpal = require('../../../../vorpal-init'); | ||
log = []; | ||
cmdInstance = { | ||
log: (msg: string) => { | ||
log.push(msg); | ||
} | ||
}; | ||
cmdInstanceLogSpy = sinon.spy(cmdInstance, 'log'); | ||
auth.site = new Site(); | ||
telemetry = null; | ||
}); | ||
|
||
afterEach(() => { | ||
Utils.restore([ | ||
vorpal.find, | ||
request.post | ||
]); | ||
}); | ||
|
||
after(() => { | ||
Utils.restore([ | ||
appInsights.trackEvent, | ||
auth.getAccessToken, | ||
auth.restoreAuth, | ||
]); | ||
}); | ||
|
||
it('has correct name', () => { | ||
assert.equal(command.name.startsWith(commands.HUBSITE_THEME_SYNC), true); | ||
}); | ||
|
||
it('has a description', () => { | ||
assert.notEqual(command.description, null); | ||
}); | ||
|
||
it('calls telemetry', (done) => { | ||
cmdInstance.action = command.action(); | ||
cmdInstance.action({ options: {} }, () => { | ||
try { | ||
assert(trackEvent.called); | ||
done(); | ||
} | ||
catch (e) { | ||
done(e); | ||
} | ||
}); | ||
}); | ||
|
||
it('logs correct telemetry event', (done) => { | ||
cmdInstance.action = command.action(); | ||
cmdInstance.action({ options: {} }, () => { | ||
try { | ||
assert.equal(telemetry.name, commands.HUBSITE_THEME_SYNC); | ||
done(); | ||
} | ||
catch (e) { | ||
done(e); | ||
} | ||
}); | ||
}); | ||
|
||
it('aborts when not connected to a SharePoint site', (done) => { | ||
auth.site = new Site(); | ||
auth.site.connected = false; | ||
cmdInstance.action = command.action(); | ||
cmdInstance.action({ options: { debug: true } }, () => { | ||
try { | ||
assert(cmdInstanceLogSpy.calledWith(new CommandError('Connect to a SharePoint Online site first'))); | ||
done(); | ||
} | ||
catch (e) { | ||
done(e); | ||
} | ||
}); | ||
}); | ||
|
||
it('syncs hub site theme to a web', (done) => { | ||
sinon.stub(request, 'post').callsFake((opts) => { | ||
if (opts.url.indexOf(`/_api/web/SyncHubSiteTheme`) > -1) { | ||
return Promise.resolve({ "odata.null": true }); | ||
} | ||
|
||
return Promise.reject('Invalid request'); | ||
}); | ||
|
||
auth.site = new Site(); | ||
auth.site.connected = true; | ||
auth.site.url = 'https://contoso.sharepoint.com'; | ||
cmdInstance.action = command.action(); | ||
cmdInstance.action({ options: { debug: false, webUrl: 'https://contoso.sharepoint.com/sites/Project-X' } }, () => { | ||
try { | ||
assert(cmdInstanceLogSpy.notCalled); | ||
done(); | ||
} | ||
catch (e) { | ||
done(e); | ||
} | ||
}); | ||
}); | ||
|
||
it('syncs hub site theme to a web (debug)', (done) => { | ||
sinon.stub(request, 'post').callsFake((opts) => { | ||
if (opts.url.indexOf(`/_api/web/SyncHubSiteTheme`) > -1) { | ||
return Promise.resolve({ | ||
"odata.null": true | ||
}); | ||
} | ||
|
||
return Promise.reject('Invalid request'); | ||
}); | ||
|
||
auth.site = new Site(); | ||
auth.site.connected = true; | ||
auth.site.url = 'https://contoso.sharepoint.com'; | ||
cmdInstance.action = command.action(); | ||
cmdInstance.action({ options: { debug: true, webUrl: 'https://contoso.sharepoint.com/sites/Project-X' } }, () => { | ||
try { | ||
assert(cmdInstanceLogSpy.calledWith(vorpal.chalk.green('DONE'))); | ||
done(); | ||
} | ||
catch (e) { | ||
done(e); | ||
} | ||
}); | ||
}); | ||
|
||
it('correctly handles error when hub site not found', (done) => { | ||
sinon.stub(request, 'post').callsFake((opts) => { | ||
return Promise.reject({ | ||
error: { | ||
"odata.error": { | ||
"code": "-1, Microsoft.SharePoint.Client.ResourceNotFoundException", | ||
"message": { | ||
"lang": "en-US", | ||
"value": "Exception of type 'Microsoft.SharePoint.Client.ResourceNotFoundException' was thrown." | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
auth.site = new Site(); | ||
auth.site.connected = true; | ||
auth.site.url = 'https://contoso.sharepoint.com'; | ||
cmdInstance.action = command.action(); | ||
cmdInstance.action({ options: { debug: false, webUrl: 'https://contoso.sharepoint.com/sites/Project-X' } }, () => { | ||
try { | ||
assert(cmdInstanceLogSpy.calledWith(new CommandError("Exception of type 'Microsoft.SharePoint.Client.ResourceNotFoundException' was thrown."))); | ||
done(); | ||
} | ||
catch (e) { | ||
done(e); | ||
} | ||
}); | ||
}); | ||
|
||
it('supports debug mode', () => { | ||
const options = (command.options() as CommandOption[]); | ||
let containsOption = false; | ||
options.forEach(o => { | ||
if (o.option === '--debug') { | ||
containsOption = true; | ||
} | ||
}); | ||
assert(containsOption); | ||
}); | ||
|
||
it('supports specifying webUrl', () => { | ||
const options = (command.options() as CommandOption[]); | ||
let containsOption = false; | ||
options.forEach(o => { | ||
if (o.option.indexOf('--webUrl') > -1) { | ||
containsOption = true; | ||
} | ||
}); | ||
assert(containsOption); | ||
}); | ||
|
||
it('fails validation if webUrl is not specified', () => { | ||
const actual = (command.validate() as CommandValidate)({ options: { id: '9b142c22-037f-4a7f-9017-e9d8c0e34b99' } }); | ||
assert.notEqual(actual, true); | ||
}); | ||
|
||
it('fails validation if webUrl is not a valid SharePoint URL', () => { | ||
const actual = (command.validate() as CommandValidate)({ options: { id: '9b142c22-037f-4a7f-9017-e9d8c0e34b99', webUrl: 'Invalid' } }); | ||
assert.notEqual(actual, true); | ||
}); | ||
|
||
it('passed validation if webUrl is a valid SharePoint URL', () => { | ||
const actual = (command.validate() as CommandValidate)({ options: { id: '9b142c22-037f-4a7f-9017-e9d8c0e34b99', webUrl: 'https://contoso.sharepoint.com' } }); | ||
assert.equal(actual, true); | ||
}); | ||
|
||
it('has help referring to the right command', () => { | ||
const cmd: any = { | ||
log: (msg: string) => { }, | ||
prompt: () => { }, | ||
helpInformation: () => { } | ||
}; | ||
const find = sinon.stub(vorpal, 'find').callsFake(() => cmd); | ||
cmd.help = command.help(); | ||
cmd.help({}, () => { }); | ||
assert(find.calledWith(commands.HUBSITE_THEME_SYNC)); | ||
}); | ||
|
||
it('has help with examples', () => { | ||
const _log: string[] = []; | ||
const cmd: any = { | ||
log: (msg: string) => { | ||
_log.push(msg); | ||
}, | ||
prompt: () => { }, | ||
helpInformation: () => { } | ||
}; | ||
sinon.stub(vorpal, 'find').callsFake(() => cmd); | ||
cmd.help = command.help(); | ||
cmd.help({}, () => { }); | ||
let containsExamples: boolean = false; | ||
_log.forEach(l => { | ||
if (l && l.indexOf('Examples:') > -1) { | ||
containsExamples = true; | ||
} | ||
}); | ||
Utils.restore(vorpal.find); | ||
assert(containsExamples); | ||
}); | ||
|
||
it('correctly handles lack of valid access token', (done) => { | ||
Utils.restore(auth.getAccessToken); | ||
sinon.stub(auth, 'getAccessToken').callsFake(() => { return Promise.reject(new Error('Error getting access token')); }); | ||
auth.site = new Site(); | ||
auth.site.connected = true; | ||
auth.site.url = 'https://contoso-admin.sharepoint.com'; | ||
cmdInstance.action = command.action(); | ||
cmdInstance.action({ options: { debug: true, webUrl: 'https://contoso.sharepoint.com/sites/Sales', confirm: true } }, () => { | ||
try { | ||
assert(cmdInstanceLogSpy.calledWith(new CommandError('Error getting access token'))); | ||
done(); | ||
} | ||
catch (e) { | ||
done(e); | ||
} | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.