diff --git a/.vscode/launch.json b/.vscode/launch.json index c8ec8010070..69ef39fd038 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,7 +16,20 @@ // 'm365 spo site get --url /', you'd use: // "args": ["spo", "site", "get", "--url", "/"] // after debugging, revert changes so that they won't end up in your PR - "args": [], + "args": [ + "spo", + "page", + "text", + "add", + "--webUrl", + "https://contoso.sharepoint.com/sites/audienceTest", + "--pageName", + "Test5.aspx", + "--text", + "Divider", + "--section", + "1" + ], "console": "integratedTerminal", "env": { "NODE_OPTIONS": "--enable-source-maps" diff --git a/src/m365/spo/commands/page/clientsidepages.ts b/src/m365/spo/commands/page/clientsidepages.ts index 032a448b905..273e8b0cd67 100644 --- a/src/m365/spo/commands/page/clientsidepages.ts +++ b/src/m365/spo/commands/page/clientsidepages.ts @@ -265,7 +265,8 @@ function reindex(collection?: { order: number, columns?: { order: number }[], co */ export class ClientSidePage { public sections: CanvasSection[] = []; - + public pageSettings?: PageSettings; + public backgroundSettings?: BackgroundSettings; /** * Converts a json object to an escaped string appropriate for use in attributes when storing client-side controls * @@ -324,9 +325,17 @@ export class ClientSidePage { html.push(this.sections[i].toHtml()); } - html.push(""); + if (this.backgroundSettings) { + html.push(this.backgroundSettings.toHtml()); + } - return html.join(""); + if (this.pageSettings) { + html.push(this.pageSettings.toHtml()); + } + + html.push(""); + const result = html.join(""); + return result; } /** @@ -344,20 +353,27 @@ export class ClientSidePage { getBoundedDivMarkup(html, /]*data-sp-canvascontrol[^>]*?>/i, markup => { // get the control type - const ct = /controlType":(\d*?),/i.exec(markup); + const ct = /controlType":(\d*?)(,|&)/i.exec(markup); // if no control type is present this is a column which we give type 0 to let us process it - const controlType = ct == null || ct.length < 2 ? 0 : parseInt(ct[1], 10); + const controlType = ct == null || ct.length < 0 ? -1 : parseInt(ct[1], 10); let control: CanvasControl | null = null; switch (controlType) { - case 0: + case -1: // empty canvas column control = new CanvasColumn(null, 0); control.fromHtml(markup); page.mergeColumnToTree(control); break; + case 0: + // page settings + control = new PageSettings(); + control.fromHtml(markup); + page.pageSettings = control; + // page.mergeColumnToTree(control); + break; case 3: // client side webpart control = new ClientSideWebpart(""); @@ -370,6 +386,12 @@ export class ClientSidePage { control.fromHtml(markup); page.mergePartToTree(control); break; + case 14: + // BackgroundSection + control = new BackgroundSettings(); + control.fromHtml(markup); + page.backgroundSettings = control; + break; } }); @@ -544,6 +566,25 @@ abstract class CanvasControl { protected abstract getControlData(): ClientSideControlData; } +export class PageSettings extends CanvasControl { + constructor() { + super(0, "1.0"); + } + + protected getControlData(): ClientSideControlData { + return this.controlData as any; + } + + public toHtml(): string { + return `
`; + } + + public fromHtml(html: string): void { + super.fromHtml(html); + + } +} + export class CanvasColumn extends CanvasControl { constructor( @@ -615,6 +656,7 @@ export class CanvasColumn extends CanvasControl { sectionIndex: this.order, zoneIndex: this.section ? this.section.order : 0 }, + zoneGroupMetadata: this.controlData?.zoneGroupMetadata || this.column?.controlData?.zoneGroupMetadata, }; } @@ -647,6 +689,73 @@ export abstract class ClientSidePart extends CanvasControl { } } +export class BackgroundSettings extends ClientSidePart { + public propertieJson: TypedHash = {}; + protected serverProcessedContent: ServerProcessedContent | null = null; + + constructor() { + super(0, "1.0"); + } + + protected getControlData(): ClientSideControlData { + return { + controlType: this.controlType + } as any; + } + + public toHtml(): string { + // will form the value of the data-sp-webpartdata attribute + const data = { + dataVersion: this.dataVersion, + instanceId: this.id, + properties: this.propertieJson, + serverProcessedContent: this.serverProcessedContent, + }; + + + const html: string[] = []; + + html.push(`
`); + + html.push(`
`); + + html.push(`
`); + html.push("
"); + + html.push(`
`); + html.push("
"); + + html.push("
"); + html.push("
"); + + return html.join(""); + } + + private setProperties(properties: T): this { + this.propertieJson = extend(this.propertieJson, properties); + return this; + } + + public fromHtml(html: string): void { + super.fromHtml(html); + const webPartData = ClientSidePage.escapedStringToJson(getAttrValueFromString(html, "data-sp-webpartdata")); + + this.setProperties(webPartData.properties); + + if (typeof webPartData.serverProcessedContent !== "undefined") { + this.serverProcessedContent = webPartData.serverProcessedContent; + } + + if (typeof webPartData.dynamicDataPaths !== "undefined") { + this.dynamicDataPaths = webPartData.dynamicDataPaths; + } + + if (typeof webPartData.dynamicDataValues !== "undefined") { + this.dynamicDataValues = webPartData.dynamicDataValues; + } + } +} + export class ClientSideText extends ClientSidePart { private _text: string = ''; @@ -685,6 +794,7 @@ export class ClientSideText extends ClientSidePart { sectionIndex: this.column ? this.column.order : 0, zoneIndex: this.column && this.column.section ? this.column.section.order : 0 }, + zoneGroupMetadata: this.controlData?.zoneGroupMetadata || this.column?.controlData?.zoneGroupMetadata, }; } @@ -837,6 +947,7 @@ export class ClientSideWebpart extends ClientSidePart { zoneIndex: this.column && this.column.section ? this.column.section.order : 0 }, webPartId: this.webPartId, + zoneGroupMetadata: this.controlData?.zoneGroupMetadata || this.column?.controlData?.zoneGroupMetadata, }; } @@ -990,6 +1101,14 @@ interface ClientSideControlPosition { zoneIndex: number; } +interface ZoneGroupMetadata { + type: number; + isExpanded: boolean; + showDividerLine: boolean; + iconAlignment: string; + displayName?: string; +} + interface ClientSideControlData { controlType?: number; id?: string; @@ -997,6 +1116,7 @@ interface ClientSideControlData { position: ClientSideControlPosition; webPartId?: string; displayMode?: number; + zoneGroupMetadata?: ZoneGroupMetadata; } interface ClientSideWebpartData { diff --git a/src/m365/spo/commands/page/page-clientsidewebpart-add.spec.ts b/src/m365/spo/commands/page/page-clientsidewebpart-add.spec.ts index 6268c523af9..d243cb23569 100644 --- a/src/m365/spo/commands/page/page-clientsidewebpart-add.spec.ts +++ b/src/m365/spo/commands/page/page-clientsidewebpart-add.spec.ts @@ -2605,6 +2605,42 @@ describe(commands.PAGE_CLIENTSIDEWEBPART_ADD, () => { })); }); + it('adds web part to an empty column in collapsible section when order 1 specified', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/sitepages/pages/GetByUrl('sitepages/page.aspx')`) { + return { + "IsPageCheckedOutToCurrentUser": true, + "CanvasContent1": `[{"id":"emptySection","position":{"controlIndex":1,"sectionFactor":12,"sectionIndex":1,"zoneIndex":1},"zoneGroupMetadata":{"type":1,"isExpanded":true,"showDividerLine":true,"iconAlignment":"left","displayName":"Test1"},"addedFromPersistedData":true},{"controlType":0,"pageSettingsSlice":{"isDefaultDescription":true,"isDefaultThumbnail":true,"isSpellCheckEnabled":true,"globalRichTextStylingVersion":0,"rtePageSettings":{"contentVersion":4,"indentationVersion":1},"isEmailReady":false,"webPartsPageSettings":{"isTitleHeadingLevelsEnabled":false}}}]` + }; + } + + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/web/getclientsidewebparts()`) { + return clientSideWebParts; + } + + throw 'Invalid request'; + }); + + const postStub = sinon.stub(request, 'post').callsFake(async (opts) => { + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/sitepages/pages/GetByUrl('sitepages/page.aspx')/SavePageAsDraft`) { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, + { + options: { + pageName: 'page.aspx', + webUrl: 'https://contoso.sharepoint.com/sites/team-a', + webPartId: 'e377ea37-9047-43b9-8cdb-a761be2f8e09', + order: 1 + } + }); + assert.strictEqual(replaceId(JSON.stringify(postStub.lastCall.args[0].data)), '{"CanvasContent1":"[{\\"controlType\\":3,\\"displayMode\\":2,\\"id\\":\\"89c644b3-f69c-4e84-85d7-dfa04c6163b5\\",\\"position\\":{\\"controlIndex\\":1,\\"sectionFactor\\":12,\\"sectionIndex\\":1,\\"zoneIndex\\":1},\\"webPartId\\":\\"e377ea37-9047-43b9-8cdb-a761be2f8e09\\",\\"emphasis\\":{},\\"webPartData\\":{\\"dataVersion\\":\\"1.0\\",\\"description\\":\\"Display a key location on a map\\",\\"id\\":\\"e377ea37-9047-43b9-8cdb-a761be2f8e09\\",\\"instanceId\\":\\"89c644b3-f69c-4e84-85d7-dfa04c6163b5\\",\\"properties\\":{\\"pushPins\\":[],\\"maxNumberOfPushPins\\":1,\\"shouldShowPushPinTitle\\":true,\\"zoomLevel\\":12,\\"mapType\\":\\"road\\"},\\"title\\":\\"Bing maps\\"},\\"zoneGroupMetadata\\":{\\"type\\":1,\\"isExpanded\\":true,\\"showDividerLine\\":true,\\"iconAlignment\\":\\"left\\",\\"displayName\\":\\"Test1\\"}},{\\"controlType\\":0,\\"pageSettingsSlice\\":{\\"isDefaultDescription\\":true,\\"isDefaultThumbnail\\":true,\\"isSpellCheckEnabled\\":true,\\"globalRichTextStylingVersion\\":0,\\"rtePageSettings\\":{\\"contentVersion\\":4,\\"indentationVersion\\":1},\\"isEmailReady\\":false,\\"webPartsPageSettings\\":{\\"isTitleHeadingLevelsEnabled\\":false}}}]"}'); + }); + it('correctly handles sections in reverse order', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { if ((opts.url as string).indexOf(`/_api/sitepages/pages/GetByUrl('sitepages/page.aspx')`) > -1) { diff --git a/src/m365/spo/commands/page/page-clientsidewebpart-add.ts b/src/m365/spo/commands/page/page-clientsidewebpart-add.ts index 33273588927..5c6b94e7aa3 100644 --- a/src/m365/spo/commands/page/page-clientsidewebpart-add.ts +++ b/src/m365/spo/commands/page/page-clientsidewebpart-add.ts @@ -270,7 +270,8 @@ class SpoPageClientSideWebPartAddCommand extends SpoCommand { id: webPart.id, position: Object.assign({}, control.position), webPartId: webPart.webPartId, - emphasis: {} + emphasis: {}, + zoneGroupMetadata: control.zoneGroupMetadata }, webPart); if (!control.controlType) { diff --git a/src/m365/spo/commands/page/page-text-add.spec.ts b/src/m365/spo/commands/page/page-text-add.spec.ts index a1d3192fa41..d124cc8d1f2 100644 --- a/src/m365/spo/commands/page/page-text-add.spec.ts +++ b/src/m365/spo/commands/page/page-text-add.spec.ts @@ -17,7 +17,6 @@ import command from './page-text-add.js'; describe(commands.PAGE_TEXT_ADD, () => { let log: string[]; let logger: Logger; - let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; let loggerLogToStderrSpy: sinon.SinonSpy; @@ -49,7 +48,6 @@ describe(commands.PAGE_TEXT_ADD, () => { log.push(msg); } }; - loggerLogSpy = sinon.spy(logger, 'log'); loggerLogToStderrSpy = sinon.spy(logger, 'logToStderr'); }); @@ -75,7 +73,7 @@ describe(commands.PAGE_TEXT_ADD, () => { it('adds text to an empty modern page', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) > -1) { + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) { return { ListItemAllFields: { CommentsDisabled: false, @@ -140,9 +138,9 @@ describe(commands.PAGE_TEXT_ADD, () => { throw 'Invalid request'; }); - sinon.stub(request, 'post').callsFake(async (opts) => { + const postStub = sinon.stub(request, 'post').callsFake(async (opts) => { if (opts.url === "https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/sitepages/page.aspx')/ListItemAllFields") { - return {}; + return; } throw 'Invalid request'; @@ -156,12 +154,15 @@ describe(commands.PAGE_TEXT_ADD, () => { text: 'Hello world' } }); - assert(loggerLogSpy.notCalled); + + const regex = /

Hello world<\/p><\/div><\/div><\/div>/; + + assert.match(postStub.lastCall.args[0].data.CanvasContent1, regex); }); it('adds text to an empty modern page (debug)', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) > -1) { + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) { return { ListItemAllFields: { CommentsDisabled: false, @@ -227,9 +228,9 @@ describe(commands.PAGE_TEXT_ADD, () => { }); sinon.stub(request, 'post').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/sitepages/page.aspx')/ListItemAllFields`) > -1 && + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/sitepages/page.aspx')/ListItemAllFields` && JSON.stringify(opts.data).indexOf(`","position":{"controlIndex":1,"sectionFactor":12,"sectionIndex":1,"zoneIndex":1}}\\">

Hello world

"}`) > -1) { - return {}; + return; } throw 'Invalid request'; @@ -249,7 +250,7 @@ describe(commands.PAGE_TEXT_ADD, () => { it('adds text to an empty modern page on root of tenant (debug)', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`https://contoso.sharepoint.com/_api/web/GetFileByServerRelativePath(DecodedUrl='/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) > -1) { + if (opts.url === `https://contoso.sharepoint.com/_api/web/GetFileByServerRelativePath(DecodedUrl='/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) { return { ListItemAllFields: { CommentsDisabled: false, @@ -315,9 +316,9 @@ describe(commands.PAGE_TEXT_ADD, () => { }); sinon.stub(request, 'post').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`https://contoso.sharepoint.com/_api/web/GetFileByServerRelativePath(DecodedUrl='/sitepages/page.aspx')/ListItemAllFields`) > -1 && + if (opts.url === `https://contoso.sharepoint.com/_api/web/GetFileByServerRelativePath(DecodedUrl='/sitepages/page.aspx')/ListItemAllFields` && JSON.stringify(opts.data).indexOf(`","position":{"controlIndex":1,"sectionFactor":12,"sectionIndex":1,"zoneIndex":1}}\\">

Hello world

"}`) > -1) { - return {}; + return; } throw 'Invalid request'; @@ -406,9 +407,9 @@ describe(commands.PAGE_TEXT_ADD, () => { throw 'Invalid request'; }); - sinon.stub(request, 'post').callsFake(async (opts) => { + const postStub = sinon.stub(request, 'post').callsFake(async (opts) => { if (opts.url === "https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/sitepages/page.aspx')/ListItemAllFields") { - return {}; + return; } throw 'Invalid request'; @@ -422,7 +423,10 @@ describe(commands.PAGE_TEXT_ADD, () => { text: 'Hello world' } }); - assert(loggerLogSpy.notCalled); + + const regex = /

Hello world<\/p><\/div><\/div>

Hello world<\/p><\/div><\/div><\/div>/; + + assert.match(postStub.lastCall.args[0].data.CanvasContent1, regex); }); it('adds text in the specified order to a modern page which already had some text', async () => { @@ -496,9 +500,9 @@ describe(commands.PAGE_TEXT_ADD, () => { throw 'Invalid request'; }); - sinon.stub(request, 'post').callsFake(async (opts) => { + const postStub = sinon.stub(request, 'post').callsFake(async (opts) => { if (opts.url === "https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/sitepages/page.aspx')/ListItemAllFields") { - return {}; + return; } throw 'Invalid request'; @@ -513,7 +517,10 @@ describe(commands.PAGE_TEXT_ADD, () => { order: 2 } }); - assert(loggerLogSpy.notCalled); + + const regex = /

Hello world<\/p><\/div><\/div>

Hello world 1.1<\/p><\/div><\/div>

Hello world 2<\/p><\/div><\/div><\/div>/; + + assert.match(postStub.lastCall.args[0].data.CanvasContent1, regex); }); it('adds text to a modern page without specifying the page file extension', async () => { @@ -587,9 +594,9 @@ describe(commands.PAGE_TEXT_ADD, () => { throw 'Invalid request'; }); - sinon.stub(request, 'post').callsFake(async (opts) => { + const postStub = sinon.stub(request, 'post').callsFake(async (opts) => { if (opts.url === "https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/sitepages/page.aspx')/ListItemAllFields") { - return {}; + return; } throw 'Invalid request'; @@ -603,12 +610,192 @@ describe(commands.PAGE_TEXT_ADD, () => { text: 'Hello world' } }); - assert(loggerLogSpy.notCalled); + + const regex = /

Hello world<\/p><\/div><\/div><\/div>/; + + assert.match(postStub.lastCall.args[0].data.CanvasContent1, regex); + }); + + it('adds text to a modern page with existing empty collapsable section', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === + `https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) { + return { + ListItemAllFields: { + CommentsDisabled: false, + FileSystemObjectType: 0, + Id: 1, + ServerRedirectedEmbedUri: null, + ServerRedirectedEmbedUrl: '', + ContentTypeId: '0x0101009D1CB255DA76424F860D91F20E6C41180062FDF2882AB3F745ACB63105A3C623C9', + FileLeafRef: 'Home.aspx', + ComplianceAssetId: null, + WikiField: null, + Title: 'Home', + ClientSideApplicationId: 'b6917cb1-93a0-4b97-a84d-7cf49975d4ec', + PageLayoutType: 'Home', + CanvasContent1: '

', + BannerImageUrl: { + Description: '/_layouts/15/images/sitepagethumbnail.png', + Url: 'https://contoso.sharepoint.com/_layouts/15/images/sitepagethumbnail.png' + }, + Description: 'Lorem ipsum Dolor samet Lorem ipsum', + PromotedState: null, + FirstPublishedDate: null, + LayoutWebpartsContent: null, + AuthorsId: null, + AuthorsStringId: null, + OriginalSourceUrl: null, + ID: 1, + Created: '2018-01-20T09:54:41', + AuthorId: 1073741823, + Modified: '2018-04-12T12:42:47', + EditorId: 12, + OData__CopySource: null, + CheckoutUserId: null, + OData__UIVersionString: '7.0', + GUID: 'edaab907-e729-48dd-9e73-26487c0cf592' + }, + CheckInComment: '', + CheckOutType: 2, + ContentTag: '{E82A21D1-CA2C-4854-98F2-012AC0E7FA09},25,1', + CustomizedPageStatus: 1, + ETag: '"{E82A21D1-CA2C-4854-98F2-012AC0E7FA09},25"', + Exists: true, + IrmEnabled: false, + Length: '805', + Level: 1, + LinkingUri: null, + LinkingUrl: '', + MajorVersion: 7, + MinorVersion: 0, + Name: 'home.aspx', + ServerRelativeUrl: '/sites/team-a/SitePages/home.aspx', + TimeCreated: '2018-01-20T08:54:41Z', + TimeLastModified: '2018-04-12T10:42:46Z', + Title: 'Home', + UIVersion: 3584, + UIVersionLabel: '7.0', + UniqueId: 'e82a21d1-ca2c-4854-98f2-012ac0e7fa09' + }; + } + + throw 'Invalid request'; + }); + + const postStub = sinon.stub(request, 'post').callsFake(async (opts) => { + if (opts.url === "https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/sitepages/page.aspx')/ListItemAllFields") { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, + { + options: { + pageName: 'page.aspx', + webUrl: 'https://contoso.sharepoint.com/sites/team-a', + text: 'Hello world' + } + }); + + const regex = /

Hello world<\/p><\/div><\/div>

<\/div><\/div>/; + + assert.match(postStub.lastCall.args[0].data.CanvasContent1, regex); + }); + + it('adds text to a modern page with existing collapsable section with existing text webpart', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) { + return { + ListItemAllFields: { + CommentsDisabled: false, + FileSystemObjectType: 0, + Id: 1, + ServerRedirectedEmbedUri: null, + ServerRedirectedEmbedUrl: '', + ContentTypeId: '0x0101009D1CB255DA76424F860D91F20E6C41180062FDF2882AB3F745ACB63105A3C623C9', + FileLeafRef: 'Home.aspx', + ComplianceAssetId: null, + WikiField: null, + Title: 'Home', + ClientSideApplicationId: 'b6917cb1-93a0-4b97-a84d-7cf49975d4ec', + PageLayoutType: 'Home', + CanvasContent1: '

text

', + BannerImageUrl: { + Description: '/_layouts/15/images/sitepagethumbnail.png', + Url: 'https://contoso.sharepoint.com/_layouts/15/images/sitepagethumbnail.png' + }, + Description: 'Lorem ipsum Dolor samet Lorem ipsum', + PromotedState: null, + FirstPublishedDate: null, + LayoutWebpartsContent: null, + AuthorsId: null, + AuthorsStringId: null, + OriginalSourceUrl: null, + ID: 1, + Created: '2018-01-20T09:54:41', + AuthorId: 1073741823, + Modified: '2018-04-12T12:42:47', + EditorId: 12, + OData__CopySource: null, + CheckoutUserId: null, + OData__UIVersionString: '7.0', + GUID: 'edaab907-e729-48dd-9e73-26487c0cf592' + }, + CheckInComment: '', + CheckOutType: 2, + ContentTag: '{E82A21D1-CA2C-4854-98F2-012AC0E7FA09},25,1', + CustomizedPageStatus: 1, + ETag: '"{E82A21D1-CA2C-4854-98F2-012AC0E7FA09},25"', + Exists: true, + IrmEnabled: false, + Length: '805', + Level: 1, + LinkingUri: null, + LinkingUrl: '', + MajorVersion: 7, + MinorVersion: 0, + Name: 'home.aspx', + ServerRelativeUrl: '/sites/team-a/SitePages/home.aspx', + TimeCreated: '2018-01-20T08:54:41Z', + TimeLastModified: '2018-04-12T10:42:46Z', + Title: 'Home', + UIVersion: 3584, + UIVersionLabel: '7.0', + UniqueId: 'e82a21d1-ca2c-4854-98f2-012ac0e7fa09' + }; + } + + throw 'Invalid request'; + }); + + const postStub = sinon.stub(request, 'post').callsFake(async (opts) => { + if (opts.url === "https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/sitepages/page.aspx')/ListItemAllFields") { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, + { + options: { + pageName: 'page.aspx', + webUrl: 'https://contoso.sharepoint.com/sites/team-a', + text: 'Hello world' + } + }); + + const regex = /

text<\/p><\/div><\/div>

Hello world<\/p><\/div><\/div>

<\/div><\/div>/; + + assert.match(postStub.lastCall.args[0].data.CanvasContent1, regex); }); it('correctly handles OData error when adding text to a non-existing page', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/foo.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) > -1) { + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/foo.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) { throw { error: { 'odata.error': { message: { value: 'The file /sites/team-a/SitePages/foo.aspx does not exist' } } } }; } @@ -627,7 +814,7 @@ describe(commands.PAGE_TEXT_ADD, () => { it('correctly handles OData error when adding text to a page', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) > -1) { + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) { return { ListItemAllFields: { CommentsDisabled: false, @@ -708,7 +895,7 @@ describe(commands.PAGE_TEXT_ADD, () => { it('correctly handles error if target page is not a modern page', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) > -1) { + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) { return { ListItemAllFields: { CommentsDisabled: false, @@ -777,7 +964,7 @@ describe(commands.PAGE_TEXT_ADD, () => { it('correctly handles invalid section error when adding text to modern page', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) > -1) { + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) { return { ListItemAllFields: { CommentsDisabled: false, @@ -855,7 +1042,7 @@ describe(commands.PAGE_TEXT_ADD, () => { it('correctly handles invalid column error when adding text to modern page', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) > -1) { + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) { return { ListItemAllFields: { CommentsDisabled: false, @@ -934,7 +1121,7 @@ describe(commands.PAGE_TEXT_ADD, () => { it('correctly handles error when parsing modern page contents', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) > -1) { + if (opts.url === `https://contoso.sharepoint.com/sites/team-a/_api/web/GetFileByServerRelativePath(DecodedUrl='/sites/team-a/SitePages/page.aspx')?$expand=ListItemAllFields/ClientSideApplicationId`) { return { ListItemAllFields: { CommentsDisabled: false,