admin screenshots

This commit is contained in:
SPRINX0\prochazka
2025-01-31 10:32:52 +01:00
parent cd90b53627
commit cdd1c34bc2
12 changed files with 333 additions and 10 deletions

View File

@@ -32,6 +32,9 @@ module.exports = defineConfig({
case 'browse-data':
serverProcess = exec('yarn start:browse-data');
break;
case 'team':
serverProcess = exec('yarn start:team');
break;
}
await waitOn({ resources: ['http://localhost:3000'] });

View File

@@ -321,4 +321,31 @@ describe('Data browser data', () => {
cy.testid('WidgetIconPanel_cell-data').click();
cy.themeshot('collection');
});
it('Table structure editor', () => {
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.contains('Customer').rightclick();
cy.contains('Open structure').click();
cy.contains('varchar(40)');
cy.themeshot('structure');
cy.contains('EmployeeId').click();
cy.contains('Ref column - Employee');
cy.themeshot('fkeditor');
});
it('Compare database', () => {
// TODO FIX: SQL diff is not dark in dark mode
cy.contains('MySql-connection').click();
cy.contains('MyChinook').click();
cy.contains('MyChangedChinook').rightclick();
cy.contains('Compare with').click();
cy.testid('CompareModelTab_gridObjects_Customer_Customer').click();
cy.testid('WidgetIconPanel_database').click();
cy.testid('CompareModelTab_tabDdl').click();
cy.themeshot('dbcompare');
cy.contains('Settings').click();
cy.testid('CompareModelTab_tabOperations').click();
cy.themeshot('comparesettings');
});
});

View File

@@ -0,0 +1,30 @@
beforeEach(() => {
cy.visit('http://localhost:3000');
cy.viewport(1250, 900);
});
describe('Team edition tests', () => {
it('Data archive editor - macros', () => {
cy.testid('LoginPage_linkAdmin').click();
cy.testid('LoginPage_password').type('adminpwd');
cy.testid('LoginPage_submitLogin').click();
cy.testid('AdminMenuWidget_itemConnections').click();
cy.contains('New connection').click();
cy.contains('New connection').click();
cy.contains('New connection').click();
cy.testid('ConnectionDriverFields_connectionType').select('PostgreSQL');
cy.themeshot('connadmin');
cy.testid('AdminMenuWidget_itemRoles').click();
cy.contains('Permissions').click();
cy.themeshot('roleadmin');
cy.testid('AdminMenuWidget_itemAuthentication').click();
cy.contains('Add authentication').click();
cy.contains('Use database login').click();
cy.contains('Add authentication').click();
cy.contains('OAuth 2.0').click();
cy.themeshot('authadmin');
});
});

View File

@@ -0,0 +1,176 @@
/*******************************************************************************
Create Tables
********************************************************************************/
CREATE TABLE `Album`
(
`AlbumId` INT NOT NULL AUTO_INCREMENT,
`Name` NVARCHAR(160) NOT NULL,
`ArtistId` INT NOT NULL,
CONSTRAINT `PK_Album` PRIMARY KEY (`AlbumId`)
);
CREATE TABLE `Artist`
(
`ArtistId` INT NOT NULL AUTO_INCREMENT,
`Name` NVARCHAR(120),
CONSTRAINT `PK_Artist` PRIMARY KEY (`ArtistId`)
);
CREATE TABLE `Customer`
(
`CustomerId` INT NOT NULL AUTO_INCREMENT,
`FirstName` NVARCHAR(40) NOT NULL,
`LastName` NVARCHAR(20) NOT NULL,
`MiddleName` NVARCHAR(20) NOT NULL,
`Company` NVARCHAR(80),
`Street` NVARCHAR(70),
`City` NVARCHAR(40),
`State` NVARCHAR(40),
`Country` NVARCHAR(40),
`PostalCode` NVARCHAR(10),
`Phone` NVARCHAR(24),
`Fax` NVARCHAR(24),
`Email` NVARCHAR(60) NOT NULL,
`SupportRepId` INT,
CONSTRAINT `PK_Customer` PRIMARY KEY (`CustomerId`)
);
CREATE TABLE `Employee`
(
`EmployeeId` INT NOT NULL AUTO_INCREMENT,
`LastName` NVARCHAR(20) NOT NULL,
`FirstName` NVARCHAR(20) NOT NULL,
`Title` NVARCHAR(30),
`ReportsTo` INT,
`BirthDate` DATETIME,
`HireDate` DATETIME,
`Address` NVARCHAR(70),
`City` NVARCHAR(40),
`State` NVARCHAR(40),
`Country` NVARCHAR(40),
`PostalCode` NVARCHAR(10),
`Phone` NVARCHAR(24),
`Fax` NVARCHAR(24),
`Email` NVARCHAR(60),
CONSTRAINT `PK_Employee` PRIMARY KEY (`EmployeeId`)
);
CREATE TABLE `Genre`
(
`GenreId` INT NOT NULL AUTO_INCREMENT,
`Name` NVARCHAR(120),
CONSTRAINT `PK_Genre` PRIMARY KEY (`GenreId`)
);
CREATE TABLE `Genre_Backup`
(
`GenreId` INT NOT NULL AUTO_INCREMENT,
`Name` NVARCHAR(120),
CONSTRAINT `PK_GenreBackup` PRIMARY KEY (`GenreId`)
);
CREATE TABLE `Invoice`
(
`InvoiceId` INT NOT NULL AUTO_INCREMENT,
`CustomerId` INT NOT NULL,
`InvoiceDate` DATETIME NOT NULL,
`BillingAddress` NVARCHAR(70),
`BillingCity` NVARCHAR(40),
`BillingState` NVARCHAR(40),
`BillingCountry` NVARCHAR(40),
`BillingPostalCode` NVARCHAR(10),
`Total` NUMERIC(10,2) NOT NULL,
CONSTRAINT `PK_Invoice` PRIMARY KEY (`InvoiceId`)
);
CREATE TABLE `MediaType`
(
`MediaTypeId` INT NOT NULL AUTO_INCREMENT,
`Name` NVARCHAR(120),
CONSTRAINT `PK_MediaType` PRIMARY KEY (`MediaTypeId`)
);
CREATE TABLE `Playlist`
(
`PlaylistId` INT NOT NULL AUTO_INCREMENT,
`Name` NVARCHAR(120),
CONSTRAINT `PK_Playlist` PRIMARY KEY (`PlaylistId`)
);
CREATE TABLE `PlaylistTrack`
(
`PlaylistId` INT NOT NULL,
`TrackId` INT NOT NULL,
CONSTRAINT `PK_PlaylistTrack` PRIMARY KEY (`PlaylistId`, `TrackId`)
);
CREATE TABLE `Track`
(
`TrackId` INT NOT NULL AUTO_INCREMENT,
`Name` NVARCHAR(200) NOT NULL,
`AlbumId` INT,
`MediaTypeId` INT NOT NULL,
`GenreId` INT,
`Composer` NVARCHAR(220),
`Milliseconds` INT NOT NULL,
`Bytes` INT,
`UnitPrice` NUMERIC(10,2) NOT NULL,
CONSTRAINT `PK_Track` PRIMARY KEY (`TrackId`)
);
/*******************************************************************************
Create Primary Key Unique Indexes
********************************************************************************/
/*******************************************************************************
Create Foreign Keys
********************************************************************************/
ALTER TABLE `Album` ADD CONSTRAINT `FK_AlbumArtistId`
FOREIGN KEY (`ArtistId`) REFERENCES `Artist` (`ArtistId`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE INDEX `IFK_AlbumArtistId` ON `Album` (`ArtistId`);
ALTER TABLE `Customer` ADD CONSTRAINT `FK_CustomerSupportRepId`
FOREIGN KEY (`SupportRepId`) REFERENCES `Employee` (`EmployeeId`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE INDEX `IFK_CustomerSupportRepId` ON `Customer` (`SupportRepId`);
ALTER TABLE `Employee` ADD CONSTRAINT `FK_EmployeeReportsTo`
FOREIGN KEY (`ReportsTo`) REFERENCES `Employee` (`EmployeeId`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE INDEX `IFK_EmployeeReportsTo` ON `Employee` (`ReportsTo`);
ALTER TABLE `Invoice` ADD CONSTRAINT `FK_InvoiceCustomerId`
FOREIGN KEY (`CustomerId`) REFERENCES `Customer` (`CustomerId`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE INDEX `IFK_InvoiceCustomerId` ON `Invoice` (`CustomerId`);
ALTER TABLE `PlaylistTrack` ADD CONSTRAINT `FK_PlaylistTrackPlaylistId`
FOREIGN KEY (`PlaylistId`) REFERENCES `Playlist` (`PlaylistId`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE INDEX `IFK_PlaylistTrackPlaylistId` ON `PlaylistTrack` (`PlaylistId`);
ALTER TABLE `PlaylistTrack` ADD CONSTRAINT `FK_PlaylistTrackTrackId`
FOREIGN KEY (`TrackId`) REFERENCES `Track` (`TrackId`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE INDEX `IFK_PlaylistTrackTrackId` ON `PlaylistTrack` (`TrackId`);
ALTER TABLE `Track` ADD CONSTRAINT `FK_TrackAlbumId`
FOREIGN KEY (`AlbumId`) REFERENCES `Album` (`AlbumId`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE INDEX `IFK_TrackAlbumId` ON `Track` (`AlbumId`);
ALTER TABLE `Track` ADD CONSTRAINT `FK_TrackGenreId`
FOREIGN KEY (`GenreId`) REFERENCES `Genre` (`GenreId`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE INDEX `IFK_TrackGenreId` ON `Track` (`GenreId`);
ALTER TABLE `Track` ADD CONSTRAINT `FK_TrackMediaTypeId`
FOREIGN KEY (`MediaTypeId`) REFERENCES `MediaType` (`MediaTypeId`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE INDEX `IFK_TrackMediaTypeId` ON `Track` (`MediaTypeId`);

7
e2e-tests/env/team/.env vendored Normal file
View File

@@ -0,0 +1,7 @@
STORAGE_SERVER=localhost
STORAGE_USER=root
STORAGE_PASSWORD=Pwd2020Db
STORAGE_PORT=16004
STORAGE_DATABASE=DbGateInternal
STORAGE_ENGINE=mysql@dbgate-plugin-mysql
ADMIN_PASSWORD=adminpwd

View File

@@ -178,6 +178,7 @@ async function copyFolder(source, target) {
async function run() {
await initMySqlDatabase('MyChinook', path.resolve(path.join(__dirname, '../data/chinook-mysql.sql')));
await initMySqlDatabase('MyChangedChinook', path.resolve(path.join(__dirname, '../data/chinook-mysql-changed.sql')));
// await initMySqlDatabase('Northwind', path.resolve(path.join(__dirname, '../data/northwind-mysql.sql')));
// await initMySqlDatabase('Sakila', path.resolve(path.join(__dirname, '../data/sakila-mysql.sql')));

34
e2e-tests/init/team.js Normal file
View File

@@ -0,0 +1,34 @@
const dbgateApi = require('dbgate-api');
dbgateApi.initializeApiEnvironment();
const dbgatePluginMysql = require('dbgate-plugin-mysql');
dbgateApi.registerPlugins(dbgatePluginMysql);
async function initStorageDatabase() {
await dbgateApi.executeQuery({
connection: {
server: process.env.STORAGE_SERVER,
user: process.env.STORAGE_USER,
password: process.env.STORAGE_PASSWORD,
port: process.env.STORAGE_PORT,
engine: process.env.STORAGE_ENGINE,
},
sql: `drop database if exists ${process.env.STORAGE_DATABASE}`,
});
await dbgateApi.executeQuery({
connection: {
server: process.env.STORAGE_SERVER,
user: process.env.STORAGE_USER,
password: process.env.STORAGE_PASSWORD,
port: process.env.STORAGE_PORT,
engine: process.env.STORAGE_ENGINE,
},
sql: `create database ${process.env.STORAGE_DATABASE}`,
});
}
async function run() {
await initStorageDatabase();
}
dbgateApi.runScript(run);

View File

@@ -14,15 +14,26 @@
},
"scripts": {
"cy:open": "cypress open --config experimentalInteractiveRunEvents=true",
"cy:run:add-connection": "cypress run --spec cypress/e2e/add-connection.cy.js",
"cy:run:portal": "cypress run --spec cypress/e2e/portal.cy.js",
"cy:run:oauth": "cypress run --spec cypress/e2e/oauth.cy.js",
"cy:run:browse-data": "cypress run --spec cypress/e2e/browse-data.cy.js",
"cy:run:team": "cypress run --spec cypress/e2e/team.cy.js",
"start:add-connection": "cd .. && node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:portal": "cd .. && env-cmd -f e2e-tests/env/portal/.env node e2e-tests/init/portal.js && env-cmd -f e2e-tests/env/portal/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:oauth": "cd .. && env-cmd -f e2e-tests/env/oauth/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:browse-data": "cd .. && env-cmd -f e2e-tests/env/browse-data/.env node e2e-tests/init/browse-data.js && env-cmd -f e2e-tests/env/browse-data/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"test": "start-server-and-test start:add-connection http://localhost:3000 cy:run:add-connection && start-server-and-test start:portal http://localhost:3000 cy:run:portal && start-server-and-test start:oauth http://localhost:3000 cy:run:oauth && start-server-and-test start:browse-data http://localhost:3000 cy:run:browse-data",
"start:team": "cd .. && env-cmd -f e2e-tests/env/team/.env node e2e-tests/init/team.js && env-cmd -f e2e-tests/env/team/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"test:add-connection": "start-server-and-test start:add-connection http://localhost:3000 cy:run:add-connection",
"test:portal": "start-server-and-test start:portal http://localhost:3000 cy:run:portal",
"test:oauth": "start-server-and-test start:oauth http://localhost:3000 cy:run:oauth",
"test:browse-data": "start-server-and-test start:browse-data http://localhost:3000 cy:run:browse-data",
"test:team": "start-server-and-test start:team http://localhost:3000 cy:run:team",
"test": "yarn test:add-connection && yarn test:portal && yarn test:oauth && yarn test:browse-data && yarn test:team",
"test:ci": "yarn test"
},
"dependencies": {}

View File

@@ -137,9 +137,9 @@
<div class="login-link">
{#if $config?.isAdminLoginForm}
{#if isAdminPage}
<Link internalRedirect="/login.html">Log In as Regular User</Link>
<Link internalRedirect="/login.html" data-testid="LoginPage_linkRegularUser">Log In as Regular User</Link>
{:else}
<Link internalRedirect="/admin-login.html">Log In as Administrator</Link>
<Link internalRedirect="/admin-login.html" data-testid="LoginPage_linkAdmin">Log In as Administrator</Link>
{/if}
{/if}
</div>
@@ -164,17 +164,41 @@
{#if selectedConnection}
{#if selectedConnection.passwordMode == 'askUser'}
<FormTextField label="Username" name="login" autocomplete="username" saveOnInput />
<FormTextField
label="Username"
name="login"
autocomplete="username"
saveOnInput
data-testid="LoginPage_username"
/>
{/if}
{#if selectedConnection.passwordMode == 'askUser' || selectedConnection.passwordMode == 'askPassword'}
<FormPasswordField label="Password" name="password" autocomplete="current-password" saveOnInput />
<FormPasswordField
label="Password"
name="password"
autocomplete="current-password"
saveOnInput
data-testid="LoginPage_password"
/>
{/if}
{:else}
{#if !isAdminPage && workflowType == 'credentials'}
<FormTextField label="Username" name="login" autocomplete="username" saveOnInput />
<FormTextField
label="Username"
name="login"
autocomplete="username"
saveOnInput
data-testid="LoginPage_username"
/>
{/if}
{#if workflowType == 'credentials'}
<FormPasswordField label="Password" name="password" autocomplete="current-password" saveOnInput />
<FormPasswordField
label="Password"
name="password"
autocomplete="current-password"
saveOnInput
data-testid="LoginPage_password"
/>
{/if}
{/if}
@@ -199,6 +223,7 @@
{#if selectedConnection?.useRedirectDbLogin}
<FormSubmit
value="Open database login page"
data-testid="LoginPage_submitLogin"
on:click={async e => {
const state = `dbg-dblogin:${strmid}:${selectedConnection?.conid}:${$values.amoid}`;
sessionStorage.setItem('dbloginAuthState', state);
@@ -210,6 +235,7 @@
{:else if selectedConnection}
<FormSubmit
value="Log In"
data-testid="LoginPage_submitLogin"
on:click={async e => {
if (selectedConnection.passwordMode == 'askUser' || selectedConnection.passwordMode == 'askPassword') {
enableApi();
@@ -257,6 +283,7 @@
await processRedirectLogin($values.amoid);
}
}}
data-testid="LoginPage_submitLogin"
/>
{/if}
</div>
@@ -264,7 +291,11 @@
<svelte:fragment slot="bottom-buttons">
{#each availableProviders.filter(x => x.workflowType == 'anonymous' || x.workflowType == 'redirect') as provider}
<div class="loginButton" on:click={() => processSingleProvider(provider)}>
<div
class="loginButton"
on:click={() => processSingleProvider(provider)}
data-testid={`LoginPage_loginButton_${provider.name}`}
>
{provider.name}
</div>
{/each}

View File

@@ -5,7 +5,7 @@
export let vertical = false;
</script>
<div on:click|stopPropagation class="collapseButtonMarker">
<div on:click|stopPropagation class="collapseButtonMarker" {...$$restProps}>
<FontIcon
icon={collapsed
? vertical

View File

@@ -16,6 +16,7 @@
else openWebLink(href);
}}
use:contextMenu={menu}
{...$$restProps}
>
<slot />
</a>

View File

@@ -9,6 +9,7 @@
headerSlot?: number;
isHighlighted?: Function;
width?: string;
testid?: (row: any) => string;
}
</script>
@@ -173,6 +174,7 @@
<td
class:isHighlighted={col.isHighlighted && col.isHighlighted(row)}
style={col.width ? `width: ${col.width}` : undefined}
data-testid={col.testid ? col.testid(row) : undefined}
>
{#if col.component}
<svelte:component this={col.component} {...col.getProps(row)} />
@@ -237,7 +239,7 @@
table.singleLineRow tbody tr td {
white-space: nowrap;
}
table tbody {
display: block;
overflow-y: scroll;