@mcpher/gas-fakes 1.2.20 → 1.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.clasp.json +4 -0
- package/.claspignore +6 -0
- package/ex-logo.png +0 -0
- package/logo.png +0 -0
- package/package.json +2 -1
- package/run.js +35 -0
- package/src/Code.js +3 -0
- package/src/appsscript.json +5 -0
- package/src/services/advforms/fakeadvforms.js +2 -1
- package/src/services/advforms/fakeadvformsform.js +12 -0
- package/src/services/advforms/fakeresponses.js +30 -0
- package/src/services/formapp/fakecheckboxgriditem.js +105 -0
- package/src/services/formapp/fakecheckboxitem.js +2 -110
- package/src/services/formapp/fakechoice.js +36 -19
- package/src/services/formapp/fakechoiceitem.js +67 -0
- package/src/services/formapp/fakeform.js +332 -17
- package/src/services/formapp/fakeformapp.js +1 -1
- package/src/services/formapp/fakeformitem.js +62 -10
- package/src/services/formapp/fakeformresponse.js +84 -0
- package/src/services/formapp/fakegriditem.js +105 -0
- package/src/services/formapp/fakeitemresponse.js +56 -0
- package/src/services/formapp/fakelistitem.js +124 -0
- package/src/services/formapp/fakemultiplechoiceitem.js +25 -0
- package/src/services/formapp/fakepagebreakitem.js +54 -0
- package/src/services/formapp/fakescaleitem.js +121 -0
- package/src/services/formapp/fakesectionheaderitem.js +25 -0
- package/src/services/formapp/faketextitem.js +20 -0
- package/src/services/formapp/formitems.js +6 -1
- package/src/support/sxforms.js +8 -4
- package/src/support/syncit.js +2 -2
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { FakeFormItem } from './fakeformitem.js';
|
|
3
|
+
import { registerFormItem } from './formitemregistry.js';
|
|
4
|
+
import { signatureArgs } from '../../support/helpers.js';
|
|
5
|
+
import { Utils } from '../../support/utils.js';
|
|
6
|
+
import { ItemType } from '../enums/formsenums.js';
|
|
7
|
+
|
|
8
|
+
export const newFakeGridItem = (...args) => {
|
|
9
|
+
return Proxies.guard(new FakeGridItem(...args));
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @class FakeGridItem
|
|
14
|
+
* A fake for the GridItem class in Apps Script.
|
|
15
|
+
* @see https://developers.google.com/apps-script/reference/forms/grid-item
|
|
16
|
+
*/
|
|
17
|
+
export class FakeGridItem extends FakeFormItem {
|
|
18
|
+
constructor(form, itemId) {
|
|
19
|
+
super(form, itemId);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Gets the values for every column in the grid.
|
|
24
|
+
* @returns {string[]} an array of column values
|
|
25
|
+
*/
|
|
26
|
+
getColumns() {
|
|
27
|
+
const choiceQuestion = this.__resource.questionGroupItem?.grid?.columns;
|
|
28
|
+
if (!choiceQuestion || !choiceQuestion.options) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
return choiceQuestion.options.map(option => option.value);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Gets the values for every row in the grid.
|
|
36
|
+
* @returns {string[]} an array of row values
|
|
37
|
+
*/
|
|
38
|
+
getRows() {
|
|
39
|
+
const questions = this.__resource.questionGroupItem?.questions;
|
|
40
|
+
if (!questions) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
return questions.map(question => question.rowQuestion?.title || null).filter(f=>f);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Sets the columns of the grid based on an array of values.
|
|
48
|
+
* @param {string[]} columns an array of column values
|
|
49
|
+
* @returns {FakeGridItem} this item, for chaining
|
|
50
|
+
*/
|
|
51
|
+
setColumns(columns) {
|
|
52
|
+
const { nargs, matchThrow } = signatureArgs(arguments, 'GridItem.setColumns');
|
|
53
|
+
if (nargs !== 1 || !Utils.is.array(columns) || !columns.every(Utils.is.string)) {
|
|
54
|
+
matchThrow('Invalid arguments: expected a string array.');
|
|
55
|
+
}
|
|
56
|
+
if (columns.length === 0) {
|
|
57
|
+
throw new Error('The array of columns cannot be empty.');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const updatedResource = JSON.parse(JSON.stringify(this.__resource));
|
|
61
|
+
updatedResource.questionGroupItem.grid.columns.options = columns.map(c => ({ value: c }));
|
|
62
|
+
|
|
63
|
+
const updateRequest = Forms.newRequest().setUpdateItem({
|
|
64
|
+
item: updatedResource,
|
|
65
|
+
location: { index: this.getIndex() },
|
|
66
|
+
updateMask: 'questionGroupItem.grid.columns.options',
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return this.__update(updateRequest);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Sets the rows of the grid based on an array of values.
|
|
74
|
+
* @param {string[]} rows an array of row values
|
|
75
|
+
* @returns {FakeGridItem} this item, for chaining
|
|
76
|
+
*/
|
|
77
|
+
setRows(rows) {
|
|
78
|
+
const { nargs, matchThrow } = signatureArgs(arguments, 'GridItem.setRows');
|
|
79
|
+
if (nargs !== 1 || !Utils.is.array(rows) || !rows.every(Utils.is.string)) {
|
|
80
|
+
matchThrow('Invalid arguments: expected a string array.');
|
|
81
|
+
}
|
|
82
|
+
if (rows.length === 0) {
|
|
83
|
+
throw new Error('The array of rows cannot be empty.');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const updatedResource = JSON.parse(JSON.stringify(this.__resource));
|
|
87
|
+
updatedResource.questionGroupItem.questions = rows.map(r => ({
|
|
88
|
+
rowQuestion: { title: r },
|
|
89
|
+
}));
|
|
90
|
+
|
|
91
|
+
const updateRequest = Forms.newRequest().setUpdateItem({
|
|
92
|
+
item: updatedResource,
|
|
93
|
+
location: { index: this.getIndex() },
|
|
94
|
+
updateMask: 'questionGroupItem.questions',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return this.__update(updateRequest);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
toString() {
|
|
101
|
+
return 'GridItem';
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
registerFormItem(ItemType.GRID, newFakeGridItem);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeFormItem } from './fakeformitem.js';
|
|
3
|
+
|
|
4
|
+
export const newFakeItemResponse = (...args) => {
|
|
5
|
+
return Proxies.guard(new FakeItemResponse(...args));
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @class FakeItemResponse
|
|
10
|
+
* @see https://developers.google.com/apps-script/reference/forms/item-response
|
|
11
|
+
*/
|
|
12
|
+
export class FakeItemResponse {
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @param {import('./fakeformitem.js').FakeFormItem} item the item this is a response to
|
|
16
|
+
* @param {object[]} answers an array of answer objects from the Forms API response
|
|
17
|
+
*/
|
|
18
|
+
constructor(item, answers) {
|
|
19
|
+
this.__item = item;
|
|
20
|
+
this.__answers = answers; // This is now an array of answer objects
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Gets the Item object for the question that this response answers.
|
|
25
|
+
*/
|
|
26
|
+
getItem() {
|
|
27
|
+
return this.__item;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Gets the ID of the item this response is for. Note: This is a custom method for gas-fakes.
|
|
32
|
+
* @returns {string} the item's ID
|
|
33
|
+
*/
|
|
34
|
+
getId() {
|
|
35
|
+
return this.__item.getId();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Gets the answer to the question as a string.
|
|
40
|
+
* @returns {string} the response
|
|
41
|
+
*/
|
|
42
|
+
getResponse() {
|
|
43
|
+
// Flatten the 'textAnswers.answers' arrays from all answer objects.
|
|
44
|
+
// This correctly combines all row answers for a grid item.
|
|
45
|
+
const allTextAnswers = this.__answers.flatMap(
|
|
46
|
+
(answer) => answer?.textAnswers?.answers || []
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (allTextAnswers.length === 0) {
|
|
50
|
+
return '';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// For items like grids, there can be multiple answer values. The live script joins them with a comma.
|
|
54
|
+
return allTextAnswers.map(a => a.value).join(',');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { FakeFormItem } from './fakeformitem.js';
|
|
3
|
+
import { newFakeChoice } from './fakechoice.js';
|
|
4
|
+
import { registerFormItem } from './formitemregistry.js';
|
|
5
|
+
import { ItemType } from '../enums/formsenums.js';
|
|
6
|
+
|
|
7
|
+
export const newFakeListItem = (...args) => {
|
|
8
|
+
return Proxies.guard(new FakeListItem(...args));
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @class FakeListItem
|
|
13
|
+
* @see https://developers.google.com/apps-script/reference/forms/list-item
|
|
14
|
+
*/
|
|
15
|
+
export class FakeListItem extends FakeFormItem {
|
|
16
|
+
constructor(...args) {
|
|
17
|
+
super(...args);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new choice for this item.
|
|
22
|
+
* @param {string} value The value for the new choice.
|
|
23
|
+
* @param {import('./pagebreakitem.js').FakePageBreakItem | import('../formapp.js').PageNavigationType} navigation The navigation action.
|
|
24
|
+
* @returns {import('./fakechoice.js').FakeChoice} The new choice.
|
|
25
|
+
*/
|
|
26
|
+
createChoice(value, navigation) {
|
|
27
|
+
// In the real API, this creates a Choice object that is not yet attached.
|
|
28
|
+
// For the fake, we can create and return a representation of it.
|
|
29
|
+
if (navigation) {
|
|
30
|
+
let navType;
|
|
31
|
+
let pageId;
|
|
32
|
+
if (typeof navigation.getId === 'function') {
|
|
33
|
+
// It's a PageBreakItem object
|
|
34
|
+
navType = 'GO_TO_PAGE';
|
|
35
|
+
pageId = navigation.getId();
|
|
36
|
+
} else {
|
|
37
|
+
// It's a PageNavigationType enum value
|
|
38
|
+
navType = navigation.toString().toUpperCase();
|
|
39
|
+
}
|
|
40
|
+
return newFakeChoice(value, navType, pageId, this.__form, this.getType());
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return newFakeChoice(value, null, null, this.__form, this.getType());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Gets the choices for this item.
|
|
48
|
+
* @returns {import('./fakechoice.js').FakeChoice[]} The choices.
|
|
49
|
+
*/
|
|
50
|
+
getChoices() {
|
|
51
|
+
const options = this.__resource.questionItem?.question?.choiceQuestion?.options || [];
|
|
52
|
+
return options.map(option => {
|
|
53
|
+
let navType = null;
|
|
54
|
+
let pageId = null;
|
|
55
|
+
|
|
56
|
+
// Translate from API format back to Apps Script format.
|
|
57
|
+
if (option.goToSectionId) {
|
|
58
|
+
navType = 'GO_TO_PAGE';
|
|
59
|
+
pageId = option.goToSectionId;
|
|
60
|
+
} else if (option.goToAction) {
|
|
61
|
+
switch (option.goToAction) {
|
|
62
|
+
case 'NEXT_SECTION': navType = 'CONTINUE'; break;
|
|
63
|
+
case 'RESTART_FORM': navType = 'RESTART'; break;
|
|
64
|
+
case 'SUBMIT_FORM': navType = 'SUBMIT'; break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return newFakeChoice(option.value, navType, pageId, this.__form, this.getType());
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Sets the choices for the item.
|
|
72
|
+
* @param {import('./fakechoice.js').FakeChoice[]} choices The choices to set.
|
|
73
|
+
* @returns {FakeListItem} The item, for chaining.
|
|
74
|
+
*/
|
|
75
|
+
setChoices(choices) {
|
|
76
|
+
// The Forms API expects an array of {value: string}, not FakeChoice objects.
|
|
77
|
+
// We need to map the array of FakeChoice objects to the correct format.
|
|
78
|
+
const options = choices.map(choice => {
|
|
79
|
+
const option = { value: choice.getValue() };
|
|
80
|
+
// The FakeChoice object stores these as internal properties.
|
|
81
|
+
// We need to translate from Apps Script enums to the Forms API enums.
|
|
82
|
+
if (choice.__navType) {
|
|
83
|
+
const navType = choice.__navType.toUpperCase();
|
|
84
|
+
switch (navType) {
|
|
85
|
+
case 'GO_TO_PAGE':
|
|
86
|
+
// For navigating to a specific page, you ONLY set the goToSectionId.
|
|
87
|
+
// The pageId is the itemId of the PageBreakItem.
|
|
88
|
+
option.goToSectionId = choice.__pageId;
|
|
89
|
+
break;
|
|
90
|
+
case 'CONTINUE':
|
|
91
|
+
option.goToAction = 'NEXT_SECTION';
|
|
92
|
+
break;
|
|
93
|
+
default:
|
|
94
|
+
option.goToAction = navType; // For RESTART_FORM, SUBMIT_FORM
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return option;
|
|
99
|
+
});
|
|
100
|
+
const updateRequest = {
|
|
101
|
+
updateItem: {
|
|
102
|
+
item: {
|
|
103
|
+
// The item payload should contain the questionItem directly,
|
|
104
|
+
// not nested within another 'question' object.
|
|
105
|
+
questionItem: { question: { choiceQuestion: { type: 'DROP_DOWN', options } } }
|
|
106
|
+
},
|
|
107
|
+
location: { index: this.getIndex() },
|
|
108
|
+
updateMask: 'questionItem.question.choiceQuestion',
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
return this.__update(updateRequest);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Returns the string "ListItem" to identify the class.
|
|
116
|
+
* @returns {string} The string "ListItem".
|
|
117
|
+
*/
|
|
118
|
+
toString() {
|
|
119
|
+
return 'ListItem';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Register the factory function for this item type.
|
|
124
|
+
registerFormItem(ItemType.LIST, newFakeListItem);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { FakeChoiceItem } from './fakechoiceitem.js';
|
|
3
|
+
import { registerFormItem } from './formitemregistry.js';
|
|
4
|
+
import { ItemType } from '../enums/formsenums.js';
|
|
5
|
+
|
|
6
|
+
export const newFakeMultipleChoiceItem = (form, itemId) => {
|
|
7
|
+
return Proxies.guard(new FakeMultipleChoiceItem(form, itemId));
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @class FakeMultipleChoiceItem
|
|
12
|
+
* A fake for the MultipleChoiceItem class in Apps Script.
|
|
13
|
+
* @see https://developers.google.com/apps-script/reference/forms/multiple-choice-item
|
|
14
|
+
*/
|
|
15
|
+
export class FakeMultipleChoiceItem extends FakeChoiceItem {
|
|
16
|
+
constructor(form, itemId) {
|
|
17
|
+
super(form, itemId);
|
|
18
|
+
}
|
|
19
|
+
toString() {
|
|
20
|
+
return 'MultipleChoiceItem';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Register the factory function for this item type.
|
|
25
|
+
registerFormItem(ItemType.MULTIPLE_CHOICE, newFakeMultipleChoiceItem);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { FakeFormItem } from './fakeformitem.js';
|
|
3
|
+
import { registerFormItem } from './formitemregistry.js';
|
|
4
|
+
import { ItemType, PageNavigationType } from '../enums/formsenums.js';
|
|
5
|
+
|
|
6
|
+
export const newFakePageBreakItem = (...args) => {
|
|
7
|
+
return Proxies.guard(new FakePageBreakItem(...args));
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @class FakePageBreakItem
|
|
12
|
+
* @see https://developers.google.com/apps-script/reference/forms/page-break-item
|
|
13
|
+
*/
|
|
14
|
+
export class FakePageBreakItem extends FakeFormItem {
|
|
15
|
+
constructor(...args) {
|
|
16
|
+
super(...args);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Sets the page to navigate to when the user advances past this page.
|
|
21
|
+
* @param {FakePageBreakItem | import('../formapp.js').PageNavigationType} navigation The navigation action.
|
|
22
|
+
* @returns {FakePageBreakItem} The item, for chaining.
|
|
23
|
+
*/
|
|
24
|
+
setGoToPage(navigation) {
|
|
25
|
+
// There is no public Forms API endpoint to set the navigation for a PageBreakItem.
|
|
26
|
+
// This method is therefore not implementable in the fake environment.
|
|
27
|
+
throw new Error('setGoToPage is not supported for PageBreakItems via the public Google Forms API.');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Gets the page-break item that this page break navigates to.
|
|
32
|
+
* @returns {FakePageBreakItem | null} The page-break item, or null if the page does not navigate to a specific page.
|
|
33
|
+
*/
|
|
34
|
+
getGoToPage() {
|
|
35
|
+
// The API does not support setting a specific page destination for a page break.
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getPageNavigationType() {
|
|
40
|
+
// Since navigation cannot be set via the API, the navigation type will always be the default.
|
|
41
|
+
return PageNavigationType.CONTINUE;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns the string "PageBreakItem" to identify the class.
|
|
46
|
+
* @returns {string} The string "PageBreakItem".
|
|
47
|
+
*/
|
|
48
|
+
toString() {
|
|
49
|
+
return 'PageBreakItem';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Register the factory function for this item type.
|
|
54
|
+
registerFormItem(ItemType.PAGE_BREAK, newFakePageBreakItem);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { FakeFormItem } from './fakeformitem.js';
|
|
3
|
+
import { registerFormItem } from './formitemregistry.js';
|
|
4
|
+
import { ItemType } from '../enums/formsenums.js';
|
|
5
|
+
import { signatureArgs } from '../../support/helpers.js';
|
|
6
|
+
import { Utils } from '../../support/utils.js';
|
|
7
|
+
const { is } = Utils;
|
|
8
|
+
|
|
9
|
+
export const newFakeScaleItem = (...args) => {
|
|
10
|
+
return Proxies.guard(new FakeScaleItem(...args));
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @class FakeScaleItem
|
|
15
|
+
* A fake for the ScaleItem class in Apps Script.
|
|
16
|
+
* @see https://developers.google.com/apps-script/reference/forms/scale-item
|
|
17
|
+
*/
|
|
18
|
+
export class FakeScaleItem extends FakeFormItem {
|
|
19
|
+
constructor(form, itemId) {
|
|
20
|
+
super(form, itemId);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Gets the lower bound of the scale.
|
|
25
|
+
* @returns {Integer} the lower bound
|
|
26
|
+
*/
|
|
27
|
+
getLowerBound() {
|
|
28
|
+
return this.__resource.questionItem.question.scaleQuestion.low || 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Gets the upper bound of the scale.
|
|
33
|
+
* @returns {Integer} the upper bound
|
|
34
|
+
*/
|
|
35
|
+
getUpperBound() {
|
|
36
|
+
return this.__resource.questionItem.question.scaleQuestion.high;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Gets the label for the scale's lower bound.
|
|
41
|
+
* @returns {string} the label for the lower bound
|
|
42
|
+
*/
|
|
43
|
+
getLeftLabel() {
|
|
44
|
+
return this.__resource.questionItem.question.scaleQuestion.lowLabel || '';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Gets the label for the scale's upper bound.
|
|
49
|
+
* @returns {string} the label for the upper bound
|
|
50
|
+
*/
|
|
51
|
+
getRightLabel() {
|
|
52
|
+
return this.__resource.questionItem.question.scaleQuestion.highLabel || '';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Sets the scale's bounds and, optionally, labels.
|
|
57
|
+
* @param {Integer} lower the lower bound
|
|
58
|
+
* @param {Integer} upper the upper bound
|
|
59
|
+
* @returns {FakeScaleItem} this item, for chaining
|
|
60
|
+
*/
|
|
61
|
+
setBounds(lower, upper) {
|
|
62
|
+
const { nargs, matchThrow } = signatureArgs(arguments, 'ScaleItem.setBounds');
|
|
63
|
+
if (nargs !== 2 || !is.number(lower) || !is.number(upper)) {
|
|
64
|
+
matchThrow('Invalid arguments: expected two numbers.');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Truncate decimals as per documentation.
|
|
68
|
+
const lowerInt = Math.trunc(lower);
|
|
69
|
+
const upperInt = Math.trunc(upper);
|
|
70
|
+
|
|
71
|
+
// Validate bounds as per documentation.
|
|
72
|
+
if (lowerInt !== 0 && lowerInt !== 1) {
|
|
73
|
+
throw new Error('The lower bound must be 0 or 1.');
|
|
74
|
+
}
|
|
75
|
+
if (upperInt < 3 || upperInt > 10) {
|
|
76
|
+
throw new Error('The upper bound must be between 3 and 10, inclusive.');
|
|
77
|
+
}
|
|
78
|
+
const updatedResource = JSON.parse(JSON.stringify(this.__resource));
|
|
79
|
+
updatedResource.questionItem.question.scaleQuestion.low = lowerInt;
|
|
80
|
+
updatedResource.questionItem.question.scaleQuestion.high = upperInt;
|
|
81
|
+
|
|
82
|
+
const updateRequest = Forms.newRequest().setUpdateItem({
|
|
83
|
+
item: updatedResource,
|
|
84
|
+
location: { index: this.getIndex() },
|
|
85
|
+
updateMask: 'questionItem.question.scaleQuestion',
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return this.__update(updateRequest);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Sets the labels for the scale's lower and upper bounds.
|
|
93
|
+
* @param {string} lower the label for the lower bound
|
|
94
|
+
* @param {string} upper the label for the upper bound
|
|
95
|
+
* @returns {FakeScaleItem} this item, for chaining
|
|
96
|
+
*/
|
|
97
|
+
setLabels(lower, upper) {
|
|
98
|
+
const { nargs, matchThrow } = signatureArgs(arguments, 'ScaleItem.setLabels');
|
|
99
|
+
if (nargs !== 2 || !is.string(lower) || !is.string(upper)) {
|
|
100
|
+
matchThrow('Invalid arguments: expected two strings.');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const updatedResource = JSON.parse(JSON.stringify(this.__resource));
|
|
104
|
+
updatedResource.questionItem.question.scaleQuestion.lowLabel = lower;
|
|
105
|
+
updatedResource.questionItem.question.scaleQuestion.highLabel = upper;
|
|
106
|
+
|
|
107
|
+
const updateRequest = Forms.newRequest().setUpdateItem({
|
|
108
|
+
item: updatedResource,
|
|
109
|
+
location: { index: this.getIndex() },
|
|
110
|
+
updateMask: 'questionItem.question.scaleQuestion',
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return this.__update(updateRequest);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
toString() {
|
|
117
|
+
return 'ScaleItem';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
registerFormItem(ItemType.SCALE, newFakeScaleItem);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { FakeFormItem } from './fakeformitem.js';
|
|
3
|
+
import { registerFormItem } from './formitemregistry.js';
|
|
4
|
+
import { ItemType } from '../enums/formsenums.js';
|
|
5
|
+
|
|
6
|
+
export const newFakeSectionHeaderItem = (...args) => {
|
|
7
|
+
return Proxies.guard(new FakeSectionHeaderItem(...args));
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @class FakeSectionHeaderItem
|
|
12
|
+
* A fake for the SectionHeaderItem class in Apps Script.
|
|
13
|
+
* @see https://developers.google.com/apps-script/reference/forms/section-header-item
|
|
14
|
+
*/
|
|
15
|
+
export class FakeSectionHeaderItem extends FakeFormItem {
|
|
16
|
+
constructor(form, itemId) {
|
|
17
|
+
super(form, itemId);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
toString() {
|
|
21
|
+
return 'SectionHeaderItem';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
registerFormItem(ItemType.SECTION_HEADER, newFakeSectionHeaderItem);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { FakeFormItem } from './fakeformitem.js';
|
|
3
|
+
|
|
4
|
+
export const newFakeTextItem = (...args) => {
|
|
5
|
+
return Proxies.guard(new FakeTextItem(...args));
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @class FakeTextItem
|
|
10
|
+
* @see https://developers.google.com/apps-script/reference/forms/text-item
|
|
11
|
+
*/
|
|
12
|
+
export class FakeTextItem extends FakeFormItem {
|
|
13
|
+
constructor(...args) {
|
|
14
|
+
super(...args);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
toString() {
|
|
18
|
+
return 'TextItem';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -2,5 +2,10 @@
|
|
|
2
2
|
// with the formitemRegistry. By importing them here, we trigger their
|
|
3
3
|
// registration code.
|
|
4
4
|
|
|
5
|
-
import './fakecheckboxitem.js'
|
|
5
|
+
import './fakecheckboxitem.js'
|
|
6
|
+
import './fakegriditem.js';
|
|
7
|
+
import './fakecheckboxgriditem.js';
|
|
8
|
+
import './fakescaleitem.js';
|
|
9
|
+
import './fakepagebreakitem.js';
|
|
10
|
+
import './fakelistitem.js';
|
|
6
11
|
// Add other item types here as they are implemented (e.g., './faketextitem.js')
|
package/src/support/sxforms.js
CHANGED
|
@@ -12,7 +12,7 @@ import { getFormsApiClient } from '../services/advforms/formsapis.js';
|
|
|
12
12
|
|
|
13
13
|
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
14
14
|
|
|
15
|
-
export const sxForms = async (Auth, { prop, method, params, options = {} }) => {
|
|
15
|
+
export const sxForms = async (Auth, { prop, subProp, method, params, options = {} }) => {
|
|
16
16
|
|
|
17
17
|
const apiClient = getFormsApiClient();
|
|
18
18
|
|
|
@@ -24,7 +24,11 @@ export const sxForms = async (Auth, { prop, method, params, options = {} }) => {
|
|
|
24
24
|
let error;
|
|
25
25
|
|
|
26
26
|
try {
|
|
27
|
-
|
|
27
|
+
// Access the base property (e.g., 'forms'), then the sub-property if it exists (e.g., 'responses').
|
|
28
|
+
let callish = apiClient[prop];
|
|
29
|
+
if (subProp) {
|
|
30
|
+
callish = callish[subProp];
|
|
31
|
+
}
|
|
28
32
|
response = await callish[method](params, options);
|
|
29
33
|
} catch (err) {
|
|
30
34
|
error = err;
|
|
@@ -35,14 +39,14 @@ export const sxForms = async (Auth, { prop, method, params, options = {} }) => {
|
|
|
35
39
|
|
|
36
40
|
if (isRetryable && i < maxRetries - 1) {
|
|
37
41
|
const jitter = Math.floor(Math.random() * 1000);
|
|
38
|
-
syncWarn(`Retryable error on Forms API call ${prop}.${method} (status: ${response?.status}). Retrying in ${delay + jitter}ms...`);
|
|
42
|
+
syncWarn(`Retryable error on Forms API call ${prop}${subProp ? '.' + subProp : ''}.${method} (status: ${response?.status}). Retrying in ${delay + jitter}ms...`);
|
|
39
43
|
await sleep(delay + jitter);
|
|
40
44
|
delay *= 2;
|
|
41
45
|
continue;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
if (error || isRetryable) {
|
|
45
|
-
syncError(`Failed in sxForms for ${prop}.${method}`, error);
|
|
49
|
+
syncError(`Failed in sxForms for ${prop}${subProp ? '.' + subProp : ''}.${method}`, error);
|
|
46
50
|
return { data: null, response: responseSyncify(response) };
|
|
47
51
|
}
|
|
48
52
|
return { data: response.data, response: responseSyncify(response) };
|
package/src/support/syncit.js
CHANGED