@ -38,6 +38,11 @@ const widgetFull = fromAccess(AccessLevel.full);
// Holds widgets manifest content.
// Holds widgets manifest content.
let widgets : ICustomWidget [ ] = [ ] ;
let widgets : ICustomWidget [ ] = [ ] ;
// Helper function to get iframe with custom widget.
function getCustomWidgetFrame() {
return driver . findWait ( 'iframe' , 500 ) ;
}
describe ( 'CustomWidgets' , function ( ) {
describe ( 'CustomWidgets' , function ( ) {
this . timeout ( 20000 ) ;
this . timeout ( 20000 ) ;
const cleanup = setupTestSuite ( ) ;
const cleanup = setupTestSuite ( ) ;
@ -50,6 +55,8 @@ describe('CustomWidgets', function () {
return server . testingHooks . setWidgetRepositoryUrl ( url ? ` ${ widgetServerUrl } ${ url } ` : '' ) ;
return server . testingHooks . setWidgetRepositoryUrl ( url ? ` ${ widgetServerUrl } ${ url } ` : '' ) ;
}
}
before ( async function ( ) {
before ( async function ( ) {
if ( server . isExternalServer ( ) ) {
if ( server . isExternalServer ( ) ) {
this . skip ( ) ;
this . skip ( ) ;
@ -100,8 +107,14 @@ describe('CustomWidgets', function () {
// Add custom section.
// Add custom section.
await gu . addNewSection ( /Custom/ , /Table1/ , { selectBy : /TABLE1/ } ) ;
await gu . addNewSection ( /Custom/ , /Table1/ , { selectBy : /TABLE1/ } ) ;
// Override gristConfig to enable widget list.
} ) ;
await driver . executeScript ( 'window.gristConfig.enableWidgetRepository = true;' ) ;
after ( async function ( ) {
await server . testingHooks . setWidgetRepositoryUrl ( '' ) ;
} ) ;
after ( async function ( ) {
await server . testingHooks . setWidgetRepositoryUrl ( '' ) ;
} ) ;
} ) ;
after ( async function ( ) {
after ( async function ( ) {
@ -109,7 +122,7 @@ describe('CustomWidgets', function () {
} ) ;
} ) ;
// Open or close widget menu.
// Open or close widget menu.
const toggle = ( ) = > driver . find ( '.test-config-widget-select .test-select-open' ) . click ( ) ;
const toggle = async ( ) = > await driver . find Wait ( '.test-config-widget-select .test-select-open' , 1000 ) . click ( ) ;
// Get current value from widget menu.
// Get current value from widget menu.
const current = ( ) = > driver . find ( '.test-config-widget-select .test-select-open' ) . getText ( ) ;
const current = ( ) = > driver . find ( '.test-config-widget-select .test-select-open' ) . getText ( ) ;
// Get options from widget menu (must be first opened).
// Get options from widget menu (must be first opened).
@ -121,19 +134,17 @@ describe('CustomWidgets', function () {
} ;
} ;
// Get rendered content from custom section.
// Get rendered content from custom section.
const content = async ( ) = > {
const content = async ( ) = > {
const iframe = driver . find ( 'iframe' ) ;
return gu . doInIframe ( await getCustomWidgetFrame ( ) , async ( ) = > {
await driver . switchTo ( ) . frame ( iframe ) ;
const text = await driver . find ( 'body' ) . getText ( ) ;
const text = await driver . find ( 'body' ) . getText ( ) ;
return text ;
await driver . switchTo ( ) . defaultContent ( ) ;
} ) ;
return text ;
} ;
} ;
async function execute (
async function execute (
op : ( table : TableOperations ) = > Promise < any > ,
op : ( table : TableOperations ) = > Promise < any > ,
tableSelector : ( grist : any ) = > TableOperations = ( grist ) = > grist . selectedTable
tableSelector : ( grist : any ) = > TableOperations = ( grist ) = > grist . selectedTable
) {
) {
const iframe = await driver . find ( 'iframe' ) ;
return gu . doInIframe ( await getCustomWidgetFrame ( ) , async ( ) = > {
await driver . switchTo ( ) . frame ( iframe ) ;
try {
const harness = async ( done : any ) = > {
const harness = async ( done : any ) = > {
const grist = ( window as any ) . grist ;
const grist = ( window as any ) . grist ;
grist . ready ( ) ;
grist . ready ( ) ;
@ -157,9 +168,7 @@ describe('CustomWidgets', function () {
const result = await driver . executeAsyncScript ( cmd ) ;
const result = await driver . executeAsyncScript ( cmd ) ;
// done callback will return null instead of undefined
// done callback will return null instead of undefined
return result === "__undefined__" ? undefined : result ;
return result === "__undefined__" ? undefined : result ;
} finally {
} ) ;
await driver . switchTo ( ) . defaultContent ( ) ;
}
}
}
// Replace url for the Custom URL widget.
// Replace url for the Custom URL widget.
const setUrl = async ( url : string ) = > {
const setUrl = async ( url : string ) = > {
@ -206,453 +215,510 @@ describe('CustomWidgets', function () {
// Rejects new access level.
// Rejects new access level.
const reject = ( ) = > driver . find ( ".test-config-widget-access-reject" ) . click ( ) ;
const reject = ( ) = > driver . find ( ".test-config-widget-access-reject" ) . click ( ) ;
it ( 'should show widgets in dropdown' , async ( ) = > {
await gu . toggleSidePanel ( 'right' , 'open' ) ;
await driver . find ( '.test-right-tab-pagewidget' ) . click ( ) ;
await gu . waitForServer ( ) ;
await driver . find ( '.test-config-widget' ) . click ( ) ;
await gu . waitForServer ( ) ; // Wait for widgets to load.
// Selectbox should have select label.
describe ( 'RightWidgetMenu' , ( ) = > {
assert . equal ( await current ( ) , CUSTOM_URL ) ;
beforeEach ( async function ( ) {
// Override gristConfig to enable widget list.
await driver . executeScript ( 'window.gristConfig.enableWidgetRepository = true;' ) ;
// We need to be sure that widget configuration panel is open all the time.
await gu . toggleSidePanel ( 'right' , 'open' ) ;
await recreatePanel ( ) ;
await driver . findWait ( '.test-right-tab-pagewidget' , 100 ) . click ( ) ;
} ) ;
// There should be 3 options (together with Custom URL)
it ( 'should show widgets in dropdown' , async ( ) = > {
await toggle ( ) ;
await gu . toggleSidePanel ( 'right' , 'open' ) ;
assert . deepEqual ( await options ( ) , [ CUSTOM_URL , widget1 . name , widget2 . name ] ) ;
await driver . find ( '.test-right-tab-pagewidget' ) . click ( ) ;
await toggle ( ) ;
await gu . waitForServer ( ) ;
} ) ;
await driver . find ( '.test-config-widget' ) . click ( ) ;
await gu . waitForServer ( ) ; // Wait for widgets to load.
it ( 'should switch between widgets' , async ( ) = > {
// Selectbox should have select label.
// Test custom URL.
assert . equal ( await current ( ) , CUSTOM_URL ) ;
await toggle ( ) ;
await select ( CUSTOM_URL ) ;
assert . equal ( await current ( ) , CUSTOM_URL ) ;
assert . equal ( await getUrl ( ) , '' ) ;
await setUrl ( '/200' ) ;
assert . equal ( await content ( ) , 'OK' ) ;
// Test first widget.
await toggle ( ) ;
await select ( widget1 . name ) ;
assert . equal ( await current ( ) , widget1 . name ) ;
assert . equal ( await content ( ) , widget1 . name ) ;
// Test second widget.
await toggle ( ) ;
await select ( widget2 . name ) ;
assert . equal ( await current ( ) , widget2 . name ) ;
assert . equal ( await content ( ) , widget2 . name ) ;
// Go back to Custom URL.
await toggle ( ) ;
await select ( CUSTOM_URL ) ;
assert . equal ( await getUrl ( ) , '' ) ;
assert . equal ( await current ( ) , CUSTOM_URL ) ;
await setUrl ( '/200' ) ;
assert . equal ( await content ( ) , 'OK' ) ;
// Clear url and test if message page is shown.
await setUrl ( '' ) ;
assert . equal ( await current ( ) , CUSTOM_URL ) ;
assert . isTrue ( ( await content ( ) ) . startsWith ( 'Custom widget' ) ) ; // start page
await recreatePanel ( ) ;
assert . equal ( await current ( ) , CUSTOM_URL ) ;
await gu . undo ( 7 ) ;
} ) ;
it ( 'should support theme variables' , async ( ) = > {
// There should be 3 options (together with Custom URL)
widgets = [ widgetWithTheme ] ;
await toggle ( ) ;
await useManifest ( manifestEndpoint ) ;
assert . deepEqual ( await options ( ) , [ CUSTOM_URL , widget1 . name , widget2 . name ] ) ;
await recreatePanel ( ) ;
await toggle ( ) ;
await toggle ( ) ;
} ) ;
await select ( widgetWithTheme . name ) ;
assert . equal ( await current ( ) , widgetWithTheme . name ) ;
assert . equal ( await content ( ) , widgetWithTheme . name ) ;
const getWidgetColor = async ( ) = > {
const iframe = driver . find ( 'iframe' ) ;
await driver . switchTo ( ) . frame ( iframe ) ;
const color = await driver . find ( 'span' ) . getCssValue ( 'color' ) ;
await driver . switchTo ( ) . defaultContent ( ) ;
return color ;
} ;
// Check that the widget is using the text color from the GristLight theme.
it ( 'should switch between widgets' , async ( ) = > {
assert . equal ( await getWidgetColor ( ) , 'rgba(38, 38, 51, 1)' ) ;
// Test custom URL.
await toggle ( ) ;
await select ( CUSTOM_URL ) ;
assert . equal ( await current ( ) , CUSTOM_URL ) ;
assert . equal ( await getUrl ( ) , '' ) ;
await setUrl ( '/200' ) ;
assert . equal ( await content ( ) , 'OK' ) ;
// Switch the theme to GristDark.
// Test first widget.
await gu . setGristTheme ( { appearance : 'dark' , syncWithOS : false } ) ;
await toggle ( ) ;
await driver . navigate ( ) . back ( ) ;
await select ( widget1 . name ) ;
await gu . waitForDocToLoad ( ) ;
assert . equal ( await current ( ) , widget1 . name ) ;
assert . equal ( await content ( ) , widget1 . name ) ;
// Check that the span is using the text color from the GristDark theme.
// Test second widget.
assert . equal ( await getWidgetColor ( ) , 'rgba(239, 239, 239, 1)' ) ;
await toggle ( ) ;
await select ( widget2 . name ) ;
assert . equal ( await current ( ) , widget2 . name ) ;
assert . equal ( await content ( ) , widget2 . name ) ;
// Switch back to GristLight.
// Go back to Custom URL.
await gu . setGristTheme ( { appearance : 'light' , syncWithOS : true } ) ;
await toggle ( ) ;
await driver . navigate ( ) . back ( ) ;
await select ( CUSTOM_URL ) ;
await gu . waitForDocToLoad ( ) ;
assert . equal ( await getUrl ( ) , '' ) ;
assert . equal ( await current ( ) , CUSTOM_URL ) ;
await setUrl ( '/200' ) ;
assert . equal ( await content ( ) , 'OK' ) ;
// Check that the widget is back to using the GristLight text color.
// Clear url and test if message page is shown.
assert . equal ( await getWidgetColor ( ) , 'rgba(38, 38, 51, 1)' ) ;
await setUrl ( '' ) ;
assert . equal ( await current ( ) , CUSTOM_URL ) ;
assert . isTrue ( ( await content ( ) ) . startsWith ( 'Custom widget' ) ) ; // start page
// Re-enable widget repository.
await recreatePanel ( ) ;
await driver . executeScript ( 'window.gristConfig.enableWidgetRepository = true;' ) ;
assert . equal ( await current ( ) , CUSTOM_URL ) ;
} ) ;
await gu . undo ( 7 ) ;
} ) ;
it ( "should support widgets that don't use the plugin api" , async ( ) = > {
it ( 'should support theme variables' , async ( ) = > {
widgets = [ widgetNoPluginApi ] ;
widgets = [ widgetWithTheme ] ;
await useManifest ( manifestEndpoint ) ;
await useManifest ( manifestEndpoint ) ;
await recreatePanel ( ) ;
await recreatePanel ( ) ;
await toggle ( ) ;
await toggle ( ) ;
await select ( widgetNoPluginApi . name ) ;
await select ( widgetWithTheme . name ) ;
assert . equal ( await current ( ) , widgetNoPluginApi . name ) ;
assert . equal ( await current ( ) , widgetWithTheme . name ) ;
assert . equal ( await content ( ) , widgetWithTheme . name ) ;
const getWidgetColor = async ( ) = > {
const iframe = driver . find ( 'iframe' ) ;
await driver . switchTo ( ) . frame ( iframe ) ;
const color = await driver . find ( 'span' ) . getCssValue ( 'color' ) ;
await driver . switchTo ( ) . defaultContent ( ) ;
return color ;
} ;
// Check that the widget loaded and its iframe is visible.
// Check that the widget is using the text color from the GristLight theme.
assert . equal ( await content ( ) , widgetNoPluginApi . name ) ;
assert . equal ( await getWidgetColor ( ) , 'rgba(38, 38, 51, 1)' ) ;
assert . isTrue ( await driver . find ( 'iframe' ) . isDisplayed ( ) ) ;
// Revert to original configuration.
// Switch the theme to GristDark.
widgets = [ widget1 , widget2 ] ;
await gu . setGristTheme ( { appearance : 'dark' , syncWithOS : false } ) ;
await useManifest ( manifestEndpoint ) ;
await driver . navigate ( ) . back ( ) ;
await recreatePanel ( ) ;
await gu . waitForDocToLoad ( ) ;
} ) ;
// Check that the span is using the text color from the GristDark theme.
assert . equal ( await getWidgetColor ( ) , 'rgba(239, 239, 239, 1)' ) ;
it ( 'should show error message for invalid widget url list' , async ( ) = > {
// Switch back to GristLight.
const testError = async ( url : string , error : string ) = > {
await gu . setGristTheme ( { appearance : 'light' , syncWithOS : true } ) ;
// Switch section to rebuild the creator panel.
await driver . navigate ( ) . back ( ) ;
await useManifest ( url ) ;
await gu . waitForDocToLoad ( ) ;
// Check that the widget is back to using the GristLight text color.
assert . equal ( await getWidgetColor ( ) , 'rgba(38, 38, 51, 1)' ) ;
// Re-enable widget repository.
await driver . executeScript ( 'window.gristConfig.enableWidgetRepository = true;' ) ;
} ) ;
it ( "should support widgets that don't use the plugin api" , async ( ) = > {
widgets = [ widgetNoPluginApi ] ;
await useManifest ( manifestEndpoint ) ;
await recreatePanel ( ) ;
await recreatePanel ( ) ;
assert . include ( await getErrorMessage ( ) , error ) ;
await gu . wipeToasts ( ) ;
// List should contain only a Custom URL.
await toggle ( ) ;
assert . deepEqual ( await options ( ) , [ CUSTOM_URL ] ) ;
await toggle ( ) ;
await toggle ( ) ;
} ;
await select ( widgetNoPluginApi . name ) ;
assert . equal ( await current ( ) , widgetNoPluginApi . name ) ;
await testError ( '/404' , "Remote widget list not found" ) ;
// Check that the widget loaded and its iframe is visible.
await testError ( '/500' , "Remote server returned an error" ) ;
assert . equal ( await content ( ) , widgetNoPluginApi . name ) ;
await testError ( '/401' , "Remote server returned an error" ) ;
assert . isTrue ( await driver . find ( 'iframe' ) . isDisplayed ( ) ) ;
await testError ( '/403' , "Remote server returned an error" ) ;
// Invalid content in a response.
await testError ( '/200' , "Error reading widget list" ) ;
// Reset to valid manifest.
// Revert to original configuration.
await useManifest ( manifestEndpoint ) ;
widgets = [ widget1 , widget2 ] ;
await recreatePanel ( ) ;
await useManifest ( manifestEndpoint ) ;
} ) ;
await recreatePanel ( ) ;
} ) ;
it ( 'should show widget when it was removed from list' , async ( ) = > {
it ( 'should show error message for invalid widget url list' , async ( ) = > {
// Select widget1 and then remove it from the list.
const testError = async ( url : string , error : string ) = > {
await toggle ( ) ;
// Switch section to rebuild the creator panel.
await select ( widget1 . name ) ;
await useManifest ( url ) ;
widgets = [ widget2 ] ;
await recreatePanel ( ) ;
// Invalidate cache.
assert . include ( await getErrorMessage ( ) , error ) ;
await useManifest ( manifestEndpoint ) ;
await gu . wipeToasts ( ) ;
// Toggle sections to reset creator panel and fetch list of available widgets.
// List should contain only a Custom URL.
await recreatePanel ( ) ;
await toggle ( ) ;
// But still should be selected with a correct url.
assert . deepEqual ( await options ( ) , [ CUSTOM_URL ] ) ;
assert . equal ( await current ( ) , widget1 . name ) ;
await toggle ( ) ;
assert . equal ( await content ( ) , widget1 . name ) ;
} ;
await gu . undo ( 1 ) ;
} ) ;
it ( 'should switch access level to none on new widget' , async ( ) = > {
await testError ( '/404' , "Remote widget list not found" ) ;
await toggle ( ) ;
await testError ( '/500' , "Remote server returned an error" ) ;
await select ( widget1 . name ) ;
await testError ( '/401' , "Remote server returned an error" ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
await testError ( '/403' , "Remote server returned an error" ) ;
await access ( AccessLevel . full ) ;
// Invalid content in a response.
assert . equal ( await access ( ) , AccessLevel . full ) ;
await testError ( '/200' , "Error reading widget list" ) ;
await toggle ( ) ;
await select ( widget2 . name ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
await access ( AccessLevel . full ) ;
assert . equal ( await access ( ) , AccessLevel . full ) ;
await toggle ( ) ;
await select ( CUSTOM_URL ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
await access ( AccessLevel . full ) ;
assert . equal ( await access ( ) , AccessLevel . full ) ;
await toggle ( ) ;
await select ( widget2 . name ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
await access ( AccessLevel . full ) ;
assert . equal ( await access ( ) , AccessLevel . full ) ;
await gu . undo ( 8 ) ;
} ) ;
it ( 'should prompt for access change' , async ( ) = > {
// Reset to valid manifest.
widgets = [ widget1 , widget2 , widgetFull , widgetNone , widgetRead ] ;
await useManifest ( manifestEndpoint ) ;
await useManifest ( manifestEndpoint ) ;
await recreatePanel ( ) ;
await recreatePanel ( ) ;
} ) ;
const test = async ( w : ICustomWidget ) = > {
it ( 'should show widget when it was removed from list' , async ( ) = > {
// Select widget without desired access level
// Select widget1 and then remove it from the list.
await toggle ( ) ;
await select ( widget1 . name ) ;
widgets = [ widget2 ] ;
// Invalidate cache.
await useManifest ( manifestEndpoint ) ;
// Toggle sections to reset creator panel and fetch list of available widgets.
await recreatePanel ( ) ;
// But still should be selected with a correct url.
assert . equal ( await current ( ) , widget1 . name ) ;
assert . equal ( await content ( ) , widget1 . name ) ;
await gu . undo ( 1 ) ;
} ) ;
it ( 'should switch access level to none on new widget' , async ( ) = > {
widgets = [ widget1 , widget2 ] ;
await recreatePanel ( ) ;
await toggle ( ) ;
await toggle ( ) ;
await select ( widget1 . name ) ;
await select ( widget1 . name ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
await access ( AccessLevel . full ) ;
assert . equal ( await access ( ) , AccessLevel . full ) ;
// Select one with desired access level
await toggle ( ) ;
await toggle ( ) ;
await select ( w . name ) ;
await select ( widget2 . name ) ;
// Access level should be still none (test by content which will display access level from query string)
assert . equal ( await content ( ) , AccessLevel . none ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
await access ( AccessLevel . full ) ;
assert . equal ( await access ( ) , AccessLevel . full ) ;
// Accept, and test if prompt is hidden, and level stays
await toggle ( ) ;
await accept ( ) ;
await select ( CUSTOM_URL ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await access ( ) , w . accessLevel ) ;
await access ( AccessLevel . full ) ;
assert . equal ( await access ( ) , AccessLevel . full ) ;
// Do the same, but this time reject
await toggle ( ) ;
await select ( widget2 . name ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
await access ( AccessLevel . full ) ;
assert . equal ( await access ( ) , AccessLevel . full ) ;
await gu . undo ( 8 ) ;
} ) ;
it ( 'should prompt for access change' , async ( ) = > {
widgets = [ widget1 , widget2 , widgetFull , widgetNone , widgetRead ] ;
await useManifest ( manifestEndpoint ) ;
await recreatePanel ( ) ;
const test = async ( w : ICustomWidget ) = > {
// Select widget without desired access level
await toggle ( ) ;
await select ( widget1 . name ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
// Select one with desired access level
await toggle ( ) ;
await select ( w . name ) ;
// Access level should be still none (test by content which will display access level from query string)
assert . equal ( await content ( ) , AccessLevel . none ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
// Accept, and test if prompt is hidden, and level stays
await accept ( ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , w . accessLevel ) ;
// Do the same, but this time reject
await toggle ( ) ;
await select ( widget1 . name ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
await toggle ( ) ;
await select ( w . name ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
await reject ( ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
} ;
await test ( widgetFull ) ;
await test ( widgetRead ) ;
} ) ;
it ( 'should auto accept none access level' , async ( ) = > {
// Select widget without access level
await toggle ( ) ;
await toggle ( ) ;
await select ( widget1 . name ) ;
await select ( widget1 . name ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
// Switch to one with none access level
await toggle ( ) ;
await toggle ( ) ;
await select ( w . name ) ;
await select ( widgetNone . name ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
await reject ( ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
} ;
} ) ;
await test ( widgetFull ) ;
await test ( widgetRead ) ;
} ) ;
it ( 'should auto accept none access level' , async ( ) = > {
it ( 'should show prompt when user switches sections' , async ( ) = > {
// Select widget without access level
// Select widget without access level
await toggle ( ) ;
await toggle ( ) ;
await select ( widget1 . name ) ;
await select ( widget1 . name ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
// Switch to one with none access level
await toggle ( ) ;
await select ( widgetNone . name ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
} ) ;
it ( 'should show prompt when user switches sections' , async ( ) = > {
// Switch to one with full access level
// Select widget without access level
await toggle ( ) ;
await toggle ( ) ;
await select ( widgetFull . name ) ;
await select ( widget1 . name ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
// Switch to one with full access level
await toggle ( ) ;
await select ( widgetFull . name ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
// Switch section, and test if prompt is hidden
await recreatePanel ( ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
} ) ;
it ( 'should hide prompt when user switches widget' , async ( ) = > {
// Switch section, and test if prompt is hidden
// Select widget without access level
await recreatePanel ( ) ;
await toggle ( ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
await select ( widget1 . name ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
} ) ;
// Switch to one with full access level
await toggle ( ) ;
await select ( widgetFull . name ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
// Switch to another level.
await toggle ( ) ;
await select ( widget1 . name ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
} ) ;
it ( 'should hide prompt when manually changes access level' , async ( ) = > {
it ( 'should hide prompt when user switches widget' , async ( ) = > {
// Select widget with no access level
// Select widget without access level
const selectNone = async ( ) = > {
await toggle ( ) ;
await toggle ( ) ;
await select ( widget None . name ) ;
await select ( widget1 . name ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
} ;
// Selects widget with full access level
// Switch to one with full access level
const selectFull = async ( ) = > {
await toggle ( ) ;
await toggle ( ) ;
await select ( widgetFull . name ) ;
await select ( widgetFull . name ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
} ;
await selectNone ( ) ;
// Switch to another level.
await selectFull ( ) ;
await toggle ( ) ;
await select ( widget1 . name ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
} ) ;
// Select the same level.
it ( 'should hide prompt when manually changes access level' , async ( ) = > {
await access ( AccessLevel . full ) ;
// Select widget with no access level
assert . isFalse ( await hasPrompt ( ) ) ;
const selectNone = async ( ) = > {
assert . equal ( await access ( ) , AccessLevel . full ) ;
await toggle ( ) ;
assert . equal ( await content ( ) , AccessLevel . full ) ;
await select ( widgetNone . name ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
} ;
await selectNone ( ) ;
// Selects widget with full access level
await selectFull ( ) ;
const selectFull = async ( ) = > {
await toggle ( ) ;
await select ( widgetFull . name ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
} ;
// Select the normal level, prompt should be still there, as widget needs a higher permission.
await selectNone ( ) ;
await access ( AccessLevel . read_table ) ;
await selectFull ( ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . read_table ) ;
assert . equal ( await content ( ) , AccessLevel . read_table ) ;
await selectNone ( ) ;
// Select the same level.
await selectFull ( ) ;
await access ( AccessLevel . full ) ;
assert . isFalse ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . full ) ;
assert . equal ( await content ( ) , AccessLevel . full ) ;
// Select the none level.
await selectNone ( ) ;
await access ( AccessLevel . none ) ;
await selectFull ( ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
} ) ;
it ( "should support grist.selectedTable" , async ( ) = > {
// Select the normal level, prompt should be still there, as widget needs a higher permission.
// Open a custom widget with full access.
await access ( AccessLevel . read_table ) ;
await gu . toggleSidePanel ( 'right' , 'open' ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
await driver . find ( '.test-config-widget' ) . click ( ) ;
assert . equal ( await access ( ) , AccessLevel . read_table ) ;
await gu . waitForServer ( ) ;
assert . equal ( await content ( ) , AccessLevel . read_table ) ;
await toggle ( ) ;
await select ( widget1 . name ) ;
await access ( AccessLevel . full ) ;
// Check an upsert works.
await execute ( async ( table ) = > {
await table . upsert ( {
require : { A : 'hello' } ,
fields : { A : 'goodbye' }
} ) ;
} ) ;
await gu . waitToPass ( async ( ) = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : 1 , col : 0 } ) . getText ( ) , 'goodbye' ) ;
} ) ;
// Check an update works.
await selectNone ( ) ;
await execute ( async table = > {
await selectFull ( ) ;
return table . update ( {
id : 2 ,
// Select the none level.
fields : { A : 'farewell' }
await access ( AccessLevel . none ) ;
} ) ;
assert . isTrue ( await hasPrompt ( ) ) ;
assert . equal ( await access ( ) , AccessLevel . none ) ;
assert . equal ( await content ( ) , AccessLevel . none ) ;
} ) ;
} ) ;
await gu . waitToPass ( async ( ) = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : 2 , col : 0 } ) . getText ( ) , 'farewell' ) ;
it ( 'should offer only custom url when disabled' , async ( ) = > {
await toggle ( ) ;
await select ( CUSTOM_URL ) ;
await driver . executeScript ( 'window.gristConfig.enableWidgetRepository = false;' ) ;
await recreatePanel ( ) ;
assert . isTrue ( await driver . find ( '.test-config-widget-url' ) . isDisplayed ( ) ) ;
assert . isFalse ( await driver . find ( '.test-config-widget-select' ) . isPresent ( ) ) ;
} ) ;
} ) ;
} ) ;
// Check options are passed along.
describe ( 'gristApiSupport' , async ( ) = > {
await execute ( async table = > {
beforeEach ( async function ( ) {
return table . upsert ( {
// Override gristConfig to enable widget list.
require : { } ,
await driver . executeScript ( 'window.gristConfig.enableWidgetRepository = true;' ) ;
fields : { A : 'goodbyes' }
// We need to be sure that widget configuration panel is open all the time.
} , { onMany : 'all' , allowEmptyRequire : true } ) ;
await gu . toggleSidePanel ( 'right' , 'open' ) ;
await recreatePanel ( ) ;
await driver . findWait ( '.test-right-tab-pagewidget' , 100 ) . click ( ) ;
} ) ;
} ) ;
await gu . waitToPass ( async ( ) = > {
it ( 'should set language in widget url' , async ( ) = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : 1 , col : 0 } ) . getText ( ) , 'goodbyes' ) ;
function languageMenu() {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : 2 , col : 0 } ) . getText ( ) , 'goodbyes' ) ;
return gu . currentDriver ( ) . find ( '.test-account-page-language .test-select-open' ) ;
}
async function language() {
return await gu . doInIframe ( await getCustomWidgetFrame ( ) , async ( ) = > {
const urlText = await driver . executeScript < string > ( 'return document.location.href' ) ;
const url = new URL ( urlText ) ;
return url . searchParams . get ( 'language' ) ;
} ) ;
}
async function switchLanguage ( lang : string ) {
await gu . openProfileSettingsPage ( ) ;
await gu . waitForServer ( ) ;
await languageMenu ( ) . click ( ) ;
await driver . findContentWait ( '.test-select-menu li' , lang , 100 ) . click ( ) ;
await gu . waitForServer ( ) ;
await driver . navigate ( ) . back ( ) ;
await gu . waitForServer ( ) ;
}
widgets = [ widget1 ] ;
await useManifest ( manifestEndpoint ) ;
await gu . openWidgetPanel ( ) ;
await toggle ( ) ;
await select ( widget1 . name ) ;
//Switch language to Polish
await switchLanguage ( 'Polski' ) ;
//Check if widgets have "pl" in url
assert . equal ( await language ( ) , 'pl' ) ;
//Switch back to English
await switchLanguage ( 'English' ) ;
//Check if widgets have "en" in url
assert . equal ( await language ( ) , 'en' ) ;
} ) ;
} ) ;
// Check a create works.
it ( "should support grist.selectedTable" , async ( ) = > {
const { id } = await execute ( async table = > {
// Open a custom widget with full access.
return table . create ( {
await gu . toggleSidePanel ( 'right' , 'open' ) ;
fields : { A : 'partA' , B : 'partB' }
await driver . find ( '.test-config-widget' ) . click ( ) ;
await gu . waitForServer ( ) ;
await toggle ( ) ;
await select ( widget1 . name ) ;
await access ( AccessLevel . full ) ;
// Check an upsert works.
await execute ( async ( table ) = > {
await table . upsert ( {
require : { A : 'hello' } ,
fields : { A : 'goodbye' }
} ) ;
} ) ;
await gu . waitToPass ( async ( ) = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : 1 , col : 0 } ) . getText ( ) , 'goodbye' ) ;
} ) ;
} ) ;
} ) as { id : number } ;
assert . equal ( id , 5 ) ;
await gu . waitToPass ( async ( ) = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : id , col : 0 } ) . getText ( ) , 'partA' ) ;
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : id , col : 1 } ) . getText ( ) , 'partB' ) ;
} ) ;
// Check a destroy works.
// Check an update works.
let result = await execute ( async table = > {
await execute ( async table = > {
await table . destroy ( 1 ) ;
return table . update ( {
} ) ;
id : 2 ,
assert . isUndefined ( result ) ;
fields : { A : 'farewell' }
await gu . waitToPass ( async ( ) = > {
} ) ;
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : id - 1 , col : 0 } ) . getText ( ) , 'partA' ) ;
} ) ;
} ) ;
await gu . waitToPass ( async ( ) = > {
result = await execute ( async table = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : 2 , col : 0 } ) . getText ( ) , 'farewell' ) ;
await table . destroy ( [ 2 ] ) ;
} ) ;
} ) ;
assert . isUndefined ( result ) ;
await gu . waitToPass ( async ( ) = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : id - 2 , col : 0 } ) . getText ( ) , 'partA' ) ;
} ) ;
// Check errors are friendly.
// Check options are passed along.
const errMessage = await execute ( async table = > {
await execute ( async table = > {
await table . create ( { fields : { ziggy : 1 } } ) ;
return table . upsert ( {
} ) ;
require : { } ,
assert . equal ( errMessage , 'Invalid column "ziggy"' ) ;
fields : { A : 'goodbyes' }
} ) ;
} , { onMany : 'all' , allowEmptyRequire : true } ) ;
} ) ;
await gu . waitToPass ( async ( ) = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : 1 , col : 0 } ) . getText ( ) , 'goodbyes' ) ;
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : 2 , col : 0 } ) . getText ( ) , 'goodbyes' ) ;
} ) ;
// Check a create works.
const { id } = await execute ( async table = > {
return table . create ( {
fields : { A : 'partA' , B : 'partB' }
} ) ;
} ) as { id : number } ;
assert . equal ( id , 5 ) ;
await gu . waitToPass ( async ( ) = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : id , col : 0 } ) . getText ( ) , 'partA' ) ;
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : id , col : 1 } ) . getText ( ) , 'partB' ) ;
} ) ;
// Check a destroy works.
let result = await execute ( async table = > {
await table . destroy ( 1 ) ;
} ) ;
assert . isUndefined ( result ) ;
await gu . waitToPass ( async ( ) = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : id - 1 , col : 0 } ) . getText ( ) , 'partA' ) ;
} ) ;
result = await execute ( async table = > {
await table . destroy ( [ 2 ] ) ;
} ) ;
assert . isUndefined ( result ) ;
await gu . waitToPass ( async ( ) = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : id - 2 , col : 0 } ) . getText ( ) , 'partA' ) ;
} ) ;
it ( "should support grist.getTable" , async ( ) = > {
// Check errors are friendly.
// Check an update on an existing table works.
const errMessage = await execute ( async table = > {
await execute ( async table = > {
await table . create ( { fields : { ziggy : 1 } } ) ;
return table . update ( {
id : 3 ,
fields : { A : 'back again' }
} ) ;
} ) ;
} , ( grist ) = > grist . getTable ( 'Table1' ) ) ;
assert . equal ( errMessage , 'Invalid column "ziggy"' ) ;
await gu . waitToPass ( async ( ) = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : 1 , col : 0 } ) . getText ( ) , 'back again' ) ;
} ) ;
} ) ;
// Check an update on a nonexistent table fails.
it ( "should support grist.getTable" , async ( ) = > {
assert . match ( String ( await execute ( async table = > {
// Check an update on an existing table works.
return table . update ( {
await execute ( async table = > {
id : 3 ,
return table . update ( {
fields : { A : 'back again' }
id : 3 ,
fields : { A : 'back again' }
} ) ;
} , ( grist ) = > grist . getTable ( 'Table1' ) ) ;
await gu . waitToPass ( async ( ) = > {
assert . equal ( await gu . getCell ( { section : 'TABLE1' , rowNum : 1 , col : 0 } ) . getText ( ) , 'back again' ) ;
} ) ;
} ) ;
} , ( grist ) = > grist . getTable ( 'Table2' ) ) ) , /Table not found/ ) ;
} ) ;
it ( "should support grist.getAccessTokens" , async ( ) = > {
// Check an update on a nonexistent table fails.
const iframe = await driver . find ( 'iframe' ) ;
assert . match ( String ( await execute ( async table = > {
await driver . switchTo ( ) . frame ( iframe ) ;
return table . update ( {
try {
id : 3 ,
const tokenResult : AccessTokenResult = await driver . executeAsyncScript (
fields : { A : 'back again' }
( done : any ) = > ( window as any ) . grist . getAccessToken ( ) . then ( done )
} ) ;
) ;
} , ( grist ) = > grist . getTable ( 'Table2' ) ) ) , /Table not found/ ) ;
assert . sameMembers ( Object . keys ( tokenResult ) , [ 'ttlMsecs' , 'token' , 'baseUrl' ] ) ;
} ) ;
const result = await fetch ( tokenResult . baseUrl + ` /tables/Table1/records?auth= ${ tokenResult . token } ` ) ;
assert . sameMembers ( Object . keys ( await result . json ( ) ) , [ 'records' ] ) ;
} finally {
await driver . switchTo ( ) . defaultContent ( ) ;
}
} ) ;
it ( 'should offer only custom url when disabled' , async ( ) = > {
it ( "should support grist.getAccessTokens" , async ( ) = > {
await toggle ( ) ;
return await gu . doInIframe ( await getCustomWidgetFrame ( ) , async ( ) = > {
await select ( CUSTOM_URL ) ;
const tokenResult : AccessTokenResult = await driver . executeAsyncScript (
await driver . executeScript ( 'window.gristConfig.enableWidgetRepository = false;' ) ;
( done : any ) = > ( window as any ) . grist . getAccessToken ( ) . then ( done )
await recreatePanel ( ) ;
) ;
assert . isTrue ( await driver . find ( '.test-config-widget-url' ) . isDisplayed ( ) ) ;
assert . sameMembers ( Object . keys ( tokenResult ) , [ 'ttlMsecs' , 'token' , 'baseUrl' ] ) ;
assert . isFalse ( await driver . find ( '.test-config-widget-select' ) . isPresent ( ) ) ;
const result = await fetch ( tokenResult . baseUrl + ` /tables/Table1/records?auth= ${ tokenResult . token } ` ) ;
assert . sameMembers ( Object . keys ( await result . json ( ) ) , [ 'records' ] ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;