mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-07-14 14:00:31 +03:00
Deprecation message in Console Folder Readme
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8131 Co-authored-by: Nicolas Beaussart <7281023+beaussan@users.noreply.github.com> GitOrigin-RevId: d0c187bb322e26105a8ecdb19da4e8478b7d09dd
This commit is contained in:
parent
7872be0e82
commit
fbb92230bb
@ -38,7 +38,7 @@ consisting of 3 components. Each has their own contributing guides:
|
||||
|
||||
2. [CLI (Go)](cli/CONTRIBUTING.md)
|
||||
|
||||
3. [Console (JavaScript)](console/README.md#contributing-to-hasura-console)
|
||||
3. [Console (JavaScript)](frontend/docs/generic-info.md#contributing-to-hasura-console)
|
||||
|
||||
All of the three components have a single version, denoted by either the git tag or a combination of branch name and git commit SHA.
|
||||
|
||||
|
1
cli-ext/.gitignore
vendored
1
cli-ext/.gitignore
vendored
@ -1,6 +1,5 @@
|
||||
bin
|
||||
build
|
||||
src/shared
|
||||
_tmptests
|
||||
node_modules
|
||||
version.json
|
||||
|
@ -4,8 +4,6 @@
|
||||
"description": "A service to generate Hasura action scaffolds",
|
||||
"main": "src/server.js",
|
||||
"scripts": {
|
||||
"get-shared-modules": "rm -rf src/shared && cp -r ../console/src/shared ./src/shared",
|
||||
"pretranspile": "npm run get-shared-modules",
|
||||
"transpile": "rm -rf build/* && babel --extensions '.ts,.js' ./src ./tests --out-dir build",
|
||||
"prebuild": "npm run transpile",
|
||||
"build": "rm -rf ./bin/* && pkg ./build/command.js --output ./bin/cli-ext -t node12-linux-x64,node12-macos-x64,node12-win-x64,node12-linux-arm64,node16-macos-arm64",
|
||||
|
@ -1,39 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"loose": true
|
||||
}
|
||||
],
|
||||
"@babel/preset-react",
|
||||
"@babel/preset-typescript"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-transform-runtime",
|
||||
"babel-plugin-styled-components",
|
||||
"transform-react-remove-prop-types",
|
||||
[
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
{
|
||||
"loose": true
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/plugin-proposal-private-methods",
|
||||
{
|
||||
"loose": true
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/plugin-proposal-private-property-in-object",
|
||||
{
|
||||
"loose": true
|
||||
}
|
||||
],
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
"extract-hoc/babel",
|
||||
"react-hot-loader/babel",
|
||||
"istanbul"
|
||||
]
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
webpack/*
|
||||
*typegen*
|
@ -1,298 +0,0 @@
|
||||
{
|
||||
"extends": ["airbnb", "prettier"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"mocha": true,
|
||||
"jest": true
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"rules": {
|
||||
"no-useless-escape": 0,
|
||||
"allowForLoopAfterthoughts": 0,
|
||||
"react/no-multi-comp": 0,
|
||||
"import/order": 0,
|
||||
"import/default": 0,
|
||||
"import/no-duplicates": 0,
|
||||
"import/named": 0,
|
||||
"import/first": 0,
|
||||
"import/namespace": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-named-as-default": 2,
|
||||
"import/extensions": 0,
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"comma-dangle": 0,
|
||||
"id-length": [
|
||||
1,
|
||||
{
|
||||
"min": 1,
|
||||
"properties": "never"
|
||||
}
|
||||
],
|
||||
"consistent-return": "off",
|
||||
"indent": "off",
|
||||
"no-console": 0,
|
||||
"arrow-parens": 0,
|
||||
"no-alert": 0,
|
||||
"no-plusplus": 0,
|
||||
"no-unsafe-negation": 0,
|
||||
"no-loop-func": 0,
|
||||
"no-lonely-if": 0,
|
||||
"no-bitwise": 0,
|
||||
"global-require": 0,
|
||||
"no-param-reassign": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-useless-return": 0,
|
||||
"no-restricted-syntax": 0,
|
||||
"no-prototype-builtins": 0,
|
||||
"array-callback-return": 0,
|
||||
"no-useless-concat": 0,
|
||||
"class-methods-use-this": 0,
|
||||
"arrow-body-style": 0,
|
||||
"prefer-template": 0,
|
||||
"prefer-spread": 0,
|
||||
"object-shorthand": 0,
|
||||
"camelcase": 0,
|
||||
"object-curly-newline": 0,
|
||||
"spaced-comment": 0,
|
||||
"prefer-destructuring": [
|
||||
"error",
|
||||
{
|
||||
"object": false,
|
||||
"array": false
|
||||
}
|
||||
],
|
||||
"prefer-rest-params": 0,
|
||||
"function-paren-newline": 0,
|
||||
"no-case-declarations": 0,
|
||||
"no-restricted-globals": 0,
|
||||
"no-unneeded-ternary": 0,
|
||||
"no-mixed-operators": 0,
|
||||
"no-return-assign": 0,
|
||||
"operator-assignment": 0,
|
||||
"strict": 0,
|
||||
"react/jsx-no-duplicate-props": 0,
|
||||
"react/jsx-filename-extension": 0,
|
||||
"react/jsx-curly-brace-presence": 0,
|
||||
"react/forbid-prop-types": 0,
|
||||
"react/require-default-props": 0,
|
||||
"react/no-unused-prop-types": 0,
|
||||
"react/no-string-refs": 0,
|
||||
"react/no-unused-state": 0,
|
||||
"react/no-array-index-key": 0,
|
||||
"react/jsx-no-bind": 0,
|
||||
"react/prop-types": 0,
|
||||
"react/prefer-stateless-function": 0,
|
||||
"react/no-unescaped-entities": 0,
|
||||
"react/sort-comp": 0,
|
||||
"react/jsx-indent": 0,
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
"jsx-a11y/click-events-have-key-events": 0,
|
||||
"jsx-a11y/no-static-element-interactions": 0,
|
||||
"jsx-a11y/no-noninteractive-element-interactions": 0,
|
||||
"jsx-a11y/label-has-for": 0,
|
||||
"jsx-a11y/anchor-is-valid": 0,
|
||||
"jsx-a11y/lang": 0,
|
||||
"jsx-a11y/alt-text": 0,
|
||||
"jsx-a11y/no-autofocus": 0,
|
||||
"max-len": 0,
|
||||
"no-continue": 0,
|
||||
"no-new": 0,
|
||||
"eqeqeq": 0,
|
||||
"no-nested-ternary": 0,
|
||||
"react/forbid-dom-props": [
|
||||
"error",
|
||||
{
|
||||
"forbid": [
|
||||
{
|
||||
"propName": "data-analytics-name",
|
||||
"message": "Analytics attributes (data-analytics-name) should be added through the Analytics component/utilities"
|
||||
},
|
||||
{
|
||||
"propName": "data-trackid",
|
||||
"message": "Analytics attributes (data-trackid) should be added through the Analytics component/utilities"
|
||||
},
|
||||
{
|
||||
"propName": "data-heap-redact-text",
|
||||
"message": "Analytics attributes (data-heap-redact-text) should be added through the Analytics component/utilities"
|
||||
},
|
||||
{
|
||||
"propName": "data-heap-redact-attributes",
|
||||
"message": "Analytics attributes (data-heap-redact-attributes) should be added through the Analytics component/utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"react/forbid-component-props": [
|
||||
"error",
|
||||
{
|
||||
"forbid": [
|
||||
{
|
||||
"propName": "data-analytics-name",
|
||||
"message": "Analytics attributes (data-analytics-name) should be added through the Analytics component/utilities"
|
||||
},
|
||||
{
|
||||
"propName": "data-trackid",
|
||||
"message": "Analytics attributes (data-trackid) should be added through the Analytics component/utilities"
|
||||
},
|
||||
{
|
||||
"propName": "data-heap-redact-text",
|
||||
"message": "Analytics attributes (data-heap-redact-text) should be added through the Analytics component/utilities"
|
||||
},
|
||||
{
|
||||
"propName": "data-heap-redact-attributes",
|
||||
"message": "Analytics attributes (data-heap-redact-attributes) should be added through the Analytics component/utilities"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"plugins": [
|
||||
"react",
|
||||
"import",
|
||||
"@typescript-eslint/eslint-plugin",
|
||||
"react-hooks",
|
||||
"testing-library"
|
||||
],
|
||||
"settings": {
|
||||
"import/resolve": {
|
||||
"moduleDirectory": ["node_modules", "src"]
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"__DEVELOPMENT__": true,
|
||||
"__CLIENT__": true,
|
||||
"__SERVER__": true,
|
||||
"__DISABLE_SSR__": true,
|
||||
"__DEVTOOLS__": true,
|
||||
"socket": true,
|
||||
"webpackIsomorphicTools": true,
|
||||
"CONSOLE_ASSET_VERSION": true
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"airbnb",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {
|
||||
"no-useless-escape": 0,
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"patterns": ["@/features/*/*", "@/new-components/*/*"]
|
||||
}
|
||||
],
|
||||
"jsx-a11y/label-has-for": [
|
||||
0,
|
||||
{
|
||||
"required": {
|
||||
"some": ["nesting", "id"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"import/order": 0,
|
||||
"import/extensions": 0,
|
||||
"import/prefer-default-export": "off",
|
||||
"react/prop-types": "off",
|
||||
"react/jsx-filename-extension": 0,
|
||||
"no-unused-vars": "off",
|
||||
"camelcase": "off",
|
||||
"no-param-reassign": "off",
|
||||
"no-use-before-define": "off",
|
||||
"consistent-return": "off",
|
||||
"import/no-extraneous-dependencies": [
|
||||
"error",
|
||||
{
|
||||
"devDependencies": [
|
||||
"src/storybook/**/*",
|
||||
"**/__tests__/**/*",
|
||||
"**/*.test.js",
|
||||
"**/*.test.jsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.ts",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.stories.tsx",
|
||||
"**/*.stories.mdx",
|
||||
"**/*.mock.tsx",
|
||||
"**/*.mock.ts"
|
||||
]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": 2,
|
||||
"@typescript-eslint/no-use-before-define": "warn",
|
||||
"@typescript-eslint/indent": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": 0,
|
||||
"@typescript-eslint/prefer-interface": 0,
|
||||
"@typescript-eslint/no-namespace": "off",
|
||||
"@typescript-eslint/camelcase": 0,
|
||||
"@typescript-eslint/explicit-member-accessibility": 0,
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
"@typescript-eslint/no-object-literal-type-assertion": "off",
|
||||
"@typescript-eslint/no-empty-interface": "off",
|
||||
"@typescript-eslint/no-parameter-properties": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/no-shadow": "error",
|
||||
"@typescript-eslint/no-useless-constructor": "error",
|
||||
"@typescript-eslint/no-unused-expressions": ["error"],
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"no-restricted-syntax": "warn",
|
||||
/**
|
||||
* Disable things that are checked by TypeScript
|
||||
*/
|
||||
"import/no-unresolved": 0,
|
||||
"getter-return": "off",
|
||||
"no-dupe-args": "off",
|
||||
"no-dupe-keys": "off",
|
||||
"no-unreachable": "off",
|
||||
"valid-typeof": "off",
|
||||
"no-const-assign": "off",
|
||||
"no-new-symbol": "off",
|
||||
"no-this-before-super": "off",
|
||||
"no-undef": "off",
|
||||
"no-dupe-class-members": "off",
|
||||
"no-redeclare": "off",
|
||||
"no-restricted-globals": "warn",
|
||||
"no-useless-constructor": "off",
|
||||
"no-unused-expressions": "off",
|
||||
"no-console": "off",
|
||||
"no-shadow": "off",
|
||||
"prefer-destructuring": "off",
|
||||
"no-plusplus": "off",
|
||||
"jsx-a11y/anchor-is-valid": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/no-static-element-interactions": "off",
|
||||
"no-new": "off",
|
||||
"no-nested-ternary": "off",
|
||||
"jsx-a11y/interactive-supports-focus": "off",
|
||||
"no-restricted-properties": "off",
|
||||
"react/no-danger": "off",
|
||||
"react/no-array-index-key": "off",
|
||||
"no-case-declarations": 0,
|
||||
"react/jsx-indent": "off",
|
||||
"arrow-body-style": "off",
|
||||
"react/require-default-props": "warn",
|
||||
"react/no-unused-prop-types": "warn",
|
||||
"jsx-a11y/role-supports-aria-props": "warn"
|
||||
}
|
||||
},
|
||||
{
|
||||
// 3) Now we enable eslint-plugin-testing-library rules or preset only for matching files!
|
||||
"files": [
|
||||
"src/**/__tests__/**/*.[jt]s?(x)",
|
||||
"src/**/?(*.)+(spec|test).[jt]s?(x)"
|
||||
],
|
||||
"extends": ["plugin:testing-library/react", "plugin:jest-dom/recommended"]
|
||||
}
|
||||
]
|
||||
}
|
41
console/.gitignore
vendored
41
console/.gitignore
vendored
@ -1,41 +0,0 @@
|
||||
# Sensitive stuff
|
||||
.env
|
||||
docker-compose.yml
|
||||
|
||||
# NPM-related stuff
|
||||
node_modules
|
||||
npm-debug.log
|
||||
.nvm
|
||||
|
||||
# Build-related stuff
|
||||
static/dist
|
||||
hasura-console-oss-1.0.0.tgz
|
||||
packages/console-oss/build/
|
||||
storybook-static
|
||||
lib/
|
||||
!src/lib
|
||||
|
||||
# Tests-related stuff
|
||||
cypress/videos
|
||||
cypress/screenshots
|
||||
.nyc_output
|
||||
coverage
|
||||
# is it needed anymore?
|
||||
test
|
||||
cypress.env.json
|
||||
|
||||
# Webpack-related stuff
|
||||
webpack-assets.json
|
||||
webpack-stats.json
|
||||
|
||||
# Text-editors-related stuff
|
||||
*.swp
|
||||
.idea/*
|
||||
**/.tern-port
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# Hasura stuff
|
||||
packages/console-oss/build/
|
||||
hasura-console-oss-1.0.0.tgz
|
@ -1,19 +0,0 @@
|
||||
exports/
|
||||
src/
|
||||
scripts/
|
||||
node_modules/
|
||||
packages/
|
||||
webpack/
|
||||
webpack-assets.json
|
||||
Makefile
|
||||
coverage
|
||||
hasuraconfig
|
||||
pro-table.js
|
||||
static/
|
||||
tsconfig.json
|
||||
bin/
|
||||
cypress/screenshots/
|
||||
.nyc_output/out.json
|
||||
.*
|
||||
hasuraconfig
|
||||
CODE_SHARING_BW_OSS_PRO.md
|
@ -1 +0,0 @@
|
||||
legacy-peer-deps=true
|
@ -1 +0,0 @@
|
||||
v16.15.1
|
@ -1 +0,0 @@
|
||||
*.min.js
|
@ -1,115 +0,0 @@
|
||||
const util = require('util');
|
||||
const webpack = require('webpack');
|
||||
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||
const path = require('path');
|
||||
const remarkSlug = require('remark-slug');
|
||||
const remarkExternalLinks = require('remark-external-links');
|
||||
|
||||
const isConfigDebugMode = process.env.STORYBOOK_CONFIG_LOG === 'debug';
|
||||
|
||||
module.exports = {
|
||||
stories: ['../src/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
||||
babel: async options => {
|
||||
if (isConfigDebugMode) {
|
||||
console.log('------BABEL--------');
|
||||
console.log(util.inspect(options, { showHidden: false, depth: null }));
|
||||
}
|
||||
return options;
|
||||
},
|
||||
webpackFinal: async config => {
|
||||
config.module.rules.push(
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
importLoaders: 2,
|
||||
modules: {
|
||||
localIdentName: '[local]___[hash:base64:5]',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
// Prefer `dart-sass`
|
||||
implementation: require('sass'),
|
||||
sassOptions: {
|
||||
outputStyle: 'expanded',
|
||||
},
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.mjs$/,
|
||||
include: /node_modules/,
|
||||
type: 'javascript/auto',
|
||||
}
|
||||
);
|
||||
config.plugins.push(
|
||||
new webpack.DefinePlugin({
|
||||
CONSOLE_ASSET_VERSION: Date.now().toString(),
|
||||
'process.hrtime': () => null,
|
||||
__CLIENT__: true,
|
||||
__SERVER__: false,
|
||||
__DEVELOPMENT__: true,
|
||||
__DEVTOOLS__: true, // <-------- DISABLE redux-devtools HERE
|
||||
})
|
||||
);
|
||||
config.resolve.plugins.push(
|
||||
new TsconfigPathsPlugin({
|
||||
configFile: path.resolve(__dirname, '../tsconfig.json'),
|
||||
})
|
||||
);
|
||||
// Fix storybook bug preventing ids to be added to MDX items https://github.com/storybookjs/storybook/issues/18395
|
||||
config.module.rules.map(rule => {
|
||||
const useWithMdxCsfLoader = rule?.use?.find(use =>
|
||||
use?.loader?.includes('@storybook/mdx1-csf')
|
||||
);
|
||||
if (useWithMdxCsfLoader) {
|
||||
useWithMdxCsfLoader.options = {
|
||||
skipCsf: false,
|
||||
remarkPlugins: [remarkSlug, remarkExternalLinks],
|
||||
};
|
||||
}
|
||||
return rule;
|
||||
});
|
||||
config.resolve.alias['@'] = path.resolve(__dirname, '../src');
|
||||
|
||||
config.node = { fs: 'empty' };
|
||||
|
||||
if (isConfigDebugMode) {
|
||||
console.log('------WEBPACK--------');
|
||||
console.log(util.inspect(newConfig, { showHidden: false, depth: null }));
|
||||
}
|
||||
|
||||
// Return the altered config
|
||||
return config;
|
||||
},
|
||||
addons: [
|
||||
{
|
||||
name: '@storybook/addon-postcss',
|
||||
options: {
|
||||
postcssLoaderOptions: {
|
||||
implementation: require('postcss'),
|
||||
postcssOptions: {
|
||||
config: path.resolve(__dirname, '../postcss-storybook.config.js'),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'@storybook/addon-docs',
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-interactions',
|
||||
'storybook-dark-mode/register',
|
||||
],
|
||||
features: {
|
||||
interactionsDebugger: true,
|
||||
babelModeV7: true,
|
||||
},
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="./images/favicon/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="./images/favicon/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="./images/favicon/favicon-16x16.png">
|
||||
<link rel="manifest" href="./images/favicon/site.webmanifest">
|
||||
<link rel="mask-icon" href="./images/favicon/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="msapplication-TileColor" content="#da532c">
|
||||
<meta name="theme-color" content="#ffffff">
|
@ -1,9 +0,0 @@
|
||||
<script>
|
||||
window.__env = {
|
||||
dataApiUrl: 'http://localhost:8080',
|
||||
apiHost: 'http://localhost',
|
||||
apiPort: '8080',
|
||||
nodeEnv: 'development',
|
||||
};
|
||||
</script>
|
||||
|
@ -1,49 +0,0 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
|
||||
rel="stylesheet">
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
<style>
|
||||
/* Fix background bug on ArgsTable header background color */
|
||||
thead.docblock-argstable-head tr th {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* Fix docblock-source background-color */
|
||||
.docblock-source,
|
||||
.prismjs {
|
||||
background-color: #1c262f !important;
|
||||
}
|
||||
|
||||
/* Add collapsible sections */
|
||||
details .mdx-collapsible-section {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
details .mdx-collapsible-section::marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
details .mdx-collapsible-section:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
details .mdx-collapsible-section__chevron {
|
||||
margin: 0;
|
||||
transform: rotate(0deg);
|
||||
transition: transform 0.1s ease-out;
|
||||
}
|
||||
|
||||
details[open] .mdx-collapsible-section__chevron {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
details .mdx-collapsible-section__label {
|
||||
margin: 0;
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
</style>
|
@ -1,93 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import produce from 'immer';
|
||||
import addons from '@storybook/addons';
|
||||
import { DocsContainer, DocsPage } from '@storybook/addon-docs';
|
||||
import { initialize, mswDecorator } from 'msw-storybook-addon';
|
||||
import theme from './theme';
|
||||
import '../src/theme/tailwind.css';
|
||||
import { store } from '../src/store';
|
||||
import '../src/components/Common/Common.module.scss';
|
||||
import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode';
|
||||
import 'react-loading-skeleton/dist/skeleton.css';
|
||||
import { ToastsHub } from '../src/new-components/Toasts';
|
||||
|
||||
// Needed to use Jest mocks (jest.fn() and so on) in Storybook stories
|
||||
import * as jest from 'jest-mock';
|
||||
global.jest = jest;
|
||||
|
||||
const channel = addons.getChannel();
|
||||
initialize();
|
||||
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: '^on.*' },
|
||||
options: {
|
||||
storySort: {
|
||||
order: ['Design system', 'Dev', 'Components', 'Hooks'],
|
||||
},
|
||||
},
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
docs: {
|
||||
container: props => {
|
||||
// Sync Docs dark mode with Storybook Manager
|
||||
const [isDark, setDark] = React.useState();
|
||||
|
||||
React.useEffect(() => {
|
||||
channel.on(DARK_MODE_EVENT_NAME, setDark);
|
||||
return () => channel.removeListener(DARK_MODE_EVENT_NAME, setDark);
|
||||
}, [channel, setDark]);
|
||||
|
||||
return (
|
||||
<div className={(isDark ? 'dark' : 'light') + ' hasura-tailwind-on'}>
|
||||
<DocsContainer
|
||||
{...props}
|
||||
context={{
|
||||
...props.context,
|
||||
storyById: id => {
|
||||
return produce(props.context.storyById(id), draft => {
|
||||
draft.parameters.docs.theme = isDark
|
||||
? theme.dark
|
||||
: theme.light;
|
||||
});
|
||||
},
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</DocsContainer>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
page: DocsPage,
|
||||
},
|
||||
darkMode: {
|
||||
dark: { ...theme.dark },
|
||||
light: { ...theme.light },
|
||||
},
|
||||
};
|
||||
|
||||
export const decorators = [
|
||||
(fn, c) => <Provider store={store}>{fn(c)}</Provider>,
|
||||
mswDecorator,
|
||||
Story => {
|
||||
document.body.classList.add('hasura-tailwind-on');
|
||||
return (
|
||||
<>
|
||||
<ToastsHub />
|
||||
<div className={'bg-legacybg'}>{Story()}</div>
|
||||
</>
|
||||
);
|
||||
},
|
||||
];
|
||||
|
||||
export const argTypes = {
|
||||
disableSnapshotTesting: {
|
||||
table: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
};
|
@ -1,78 +0,0 @@
|
||||
const commonThemeProperties = {
|
||||
fontBase:
|
||||
'"IBM Plex Sans",system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"',
|
||||
fontCode:
|
||||
'"IBM Plex Mono Regular","IBM Plex Mono",SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace',
|
||||
|
||||
brandTitle: 'Hasura',
|
||||
brandUrl: 'http://hasura.io',
|
||||
brandTarget: '_self',
|
||||
};
|
||||
|
||||
// Color theme reversed engineered from Hasura docs (https://hasura.io/docs/latest/graphql/core/index/)
|
||||
export default {
|
||||
light: {
|
||||
...commonThemeProperties,
|
||||
|
||||
base: 'light',
|
||||
|
||||
colorPrimary: '#1599e2',
|
||||
colorSecondary: '#1599e2',
|
||||
|
||||
// UI
|
||||
appBg: '#f4f5f7',
|
||||
appContentBg: 'white',
|
||||
appBorderColor: '#dadde1',
|
||||
appBorderRadius: 4,
|
||||
|
||||
// Text colors
|
||||
textColor: '#344658',
|
||||
textInverseColor: '#cbb9a7',
|
||||
textMutedColor: '#8a929b',
|
||||
|
||||
// Toolbar default and active colors
|
||||
barTextColor: '#141c22',
|
||||
barSelectedColor: '#1699e2',
|
||||
barBg: 'white',
|
||||
|
||||
// Form colors
|
||||
inputBg: 'white',
|
||||
inputBorder: '#dadde1',
|
||||
inputTextColor: '#344658',
|
||||
inputBorderRadius: 4,
|
||||
|
||||
brandImage: './images/hasura-storybook-dark.svg',
|
||||
},
|
||||
dark: {
|
||||
...commonThemeProperties,
|
||||
|
||||
base: 'dark',
|
||||
|
||||
colorPrimary: '#1599e2',
|
||||
colorSecondary: '#1599e2',
|
||||
|
||||
// UI
|
||||
appBg: '#23303d',
|
||||
appContentBg: '#131c22',
|
||||
appBorderColor: '#616770',
|
||||
appBorderRadius: 4,
|
||||
|
||||
// Text colors
|
||||
textColor: '#dce2e8',
|
||||
textInverseColor: '#231d17',
|
||||
textMutedColor: '#92979c',
|
||||
|
||||
// Toolbar default and active colors
|
||||
barTextColor: '#dce2e8',
|
||||
barSelectedColor: '#dce2e8',
|
||||
barBg: '#131c22',
|
||||
|
||||
// Form colors
|
||||
inputBg: '#23303d',
|
||||
inputBorder: '#616770',
|
||||
inputTextColor: '#dce2e8',
|
||||
inputBorderRadius: 4,
|
||||
|
||||
brandImage: './images/hasura-storybook-light.svg',
|
||||
},
|
||||
};
|
@ -1,43 +0,0 @@
|
||||
export PATH := node_modules/.bin:$(PATH)
|
||||
DIST_PATH ?= ./static/dist
|
||||
BUCKET_NAME ?= graphql-engine-cdn.hasura.io
|
||||
VERSION ?= $(shell ../scripts/get-version.sh)
|
||||
NODE_OPTIONS=--max-old-space-size=4096
|
||||
|
||||
all: deps build
|
||||
|
||||
deps:
|
||||
NODE_OPTIONS=$(NODE_OPTIONS) npm ci
|
||||
|
||||
ci-deps:
|
||||
if [ ! -d "node_modules" ]; then NODE_OPTIONS=$(NODE_OPTIONS) npm ci; fi
|
||||
|
||||
build:
|
||||
NODE_OPTIONS=$(NODE_OPTIONS) npm run build
|
||||
|
||||
jest:
|
||||
NODE_OPTIONS=$(NODE_OPTIONS) npm run jest -- --runInBand
|
||||
|
||||
test:
|
||||
NODE_OPTIONS=$(NODE_OPTIONS) npm run dev & NODE_OPTIONS=$(NODE_OPTIONS) npm run test
|
||||
|
||||
# to be run inside circle-ci
|
||||
ci-copy-assets:
|
||||
mkdir -p /build/_console_output
|
||||
cp $(DIST_PATH)/* /build/_console_output/
|
||||
echo "$(VERSION)" > /build/_console_output/version.txt
|
||||
|
||||
server-build: node_modules $(DIST_PATH)/common
|
||||
rm -rf "$(DIST_PATH)/versioned"
|
||||
npm run build
|
||||
mkdir -p "$(DIST_PATH)/versioned"
|
||||
cp "$(DIST_PATH)"/*.js "$(DIST_PATH)"/*.css "$(DIST_PATH)/versioned/"
|
||||
gzip -r -f "$(DIST_PATH)/versioned"
|
||||
|
||||
$(DIST_PATH)/common:
|
||||
mkdir -p $(DIST_PATH)
|
||||
gsutil -m cp -r gs://$(BUCKET_NAME)/console/assets/common "$(DIST_PATH)"
|
||||
touch $@
|
||||
|
||||
node_modules: package.json package-lock.json
|
||||
NODE_OPTIONS=$(NODE_OPTIONS) npm ci
|
@ -1,14 +1,6 @@
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
# Hasura Console
|
||||
# Deprecated
|
||||
Please note that all console related code has been moved to `frontend` folder in this repo, which uses NX based build system. All old code (now deleted) in this directory has been deprecated as of Feb 7, 2023, or commits since the [v2.18.0-beta.1 tag](https://github.com/hasura/graphql-engine/tree/v2.18.0-beta.1). Please refer to this new [Readme](https://github.com/hasura/graphql-engine/blob/master/frontend/README.md).
|
||||
|
||||
The Hasura console is an admin dashboard to manage the connected database and to try out GraphQL APIs. It is a React application bundled with Webpack and the state is managed mostly using Redux.
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Generic info](./docs/console-generic-info.md)
|
||||
- [Development Tooling](https://main--614d7904644d03004addd43b.chromatic.com/?path=/story/dev-tooling--page)
|
||||
- [Design System's Storybook](https://main--614d7904644d03004addd43b.chromatic.com)
|
||||
- [How to create/document new Components](./src/docs/dev/ComponentDoc.stories.mdx)
|
||||
- [Cypress Dashboard](https://dashboard.cypress.io/projects/5yiuic)
|
||||
- [Cypress README](./cypress/README.md)
|
||||
More details on how to contribute can be found [here](https://github.com/hasura/graphql-engine/blob/master/CONTRIBUTING.md)
|
||||
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"cSpell.words": ["clsx"],
|
||||
"tailwindCSS.experimental.classRegex": [
|
||||
"tw\\w+ ?= ?`([^`]*)`",
|
||||
"tw\\w+: ?`([^`]*)`"
|
||||
],
|
||||
"tailwindCSS.rootFontSize": 14
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
overwrite: true
|
||||
schema: "src/features/ControlPlane/schema.json"
|
||||
documents: "src/features/ControlPlane/queries.ts"
|
||||
generates:
|
||||
src/features/ControlPlane/generatedGraphQLTypes.ts:
|
||||
plugins: ["typescript", "typescript-operations"]
|
@ -1,40 +0,0 @@
|
||||
import { defineConfig } from 'cypress';
|
||||
|
||||
import * as customTasks from './cypress/support/tasks';
|
||||
|
||||
type ConfigOptions = Parameters<typeof defineConfig>[0];
|
||||
interface MyConfigOptions extends ConfigOptions {
|
||||
useRelativeSnapshots?: boolean;
|
||||
}
|
||||
|
||||
const myDefineConfig = (config: MyConfigOptions) => defineConfig(config);
|
||||
|
||||
export default myDefineConfig({
|
||||
env: {
|
||||
TEST_MODE: 'parallel',
|
||||
MIGRATE_URL: 'http://localhost:9693/apis/migrate',
|
||||
},
|
||||
viewportWidth: 1280,
|
||||
viewportHeight: 720,
|
||||
chromeWebSecurity: false,
|
||||
video: false,
|
||||
projectId: '5yiuic',
|
||||
numTestsKeptInMemory: 10,
|
||||
e2e: {
|
||||
// We've imported your old cypress plugins here.
|
||||
// You may want to clean this up later by importing these.
|
||||
setupNodeEvents(on, config) {
|
||||
on('task', {
|
||||
...customTasks,
|
||||
});
|
||||
|
||||
return config;
|
||||
},
|
||||
baseUrl: 'http://localhost:3000',
|
||||
specPattern: [
|
||||
'cypress/e2e/**/*test.{js,jsx,ts,tsx}',
|
||||
'cypress/support/**/*unit.test.{js,ts}',
|
||||
],
|
||||
},
|
||||
useRelativeSnapshots: true,
|
||||
});
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"plugins": ["cypress", "chai-friendly"],
|
||||
"env": {
|
||||
"cypress/globals": true
|
||||
},
|
||||
"rules": {
|
||||
"no-unused-expressions": "off",
|
||||
"chai-friendly/no-unused-expressions": "error",
|
||||
"no-underscore-dangle": "off",
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
"no-plusplus": [
|
||||
"error",
|
||||
{
|
||||
"allowForLoopAfterthoughts": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
# Test
|
||||
|
||||
## Useful resources
|
||||
- [Cypress Dashboard for the Console project](https://dashboard.cypress.io/projects/5yiuic)
|
||||
|
||||
## Running all tests to generate coverage
|
||||
|
||||
1. Set the `TEST_MODE` field in `cypress.json` to `cli`
|
||||
2. Run the command `npm run test` from the `console` directory to run all the tests.
|
||||
|
||||
## Running tests individually
|
||||
|
||||
Tests are modularized into following modules:
|
||||
|
||||
- API-Explorer
|
||||
- Data
|
||||
- Migration Mode
|
||||
- Create Table
|
||||
- Insert Browse
|
||||
- Modify Table
|
||||
- Table Relationships
|
||||
- Table and View Permissions
|
||||
- Views
|
||||
|
||||
To run the tests for the modules individually (say for create table),
|
||||
|
||||
- Go to the `cypress.json` and set the `env > TEST_MODE` variable to `ui`.
|
||||
|
||||
```json
|
||||
{
|
||||
"env": {
|
||||
"TEST_MODE": "ui"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Run the command `npm run cy:open` and click on `create-table > test.js`
|
||||
|
||||
## Writing Tests
|
||||
|
||||
- Read ups
|
||||
|
||||
- If this is your first time with cypress, check out this getting started [guide](https://docs.cypress.io/guides/getting-started/writing-your-first-test.html)
|
||||
|
||||
- Read cypress [best practices](https://docs.cypress.io/guides/references/best-practices.html)
|
||||
|
||||
- File Structure
|
||||
|
||||
The top-level directories in [console/cypress](../../console/cypress) are auto-generated by cypress except [helpers](../../console/cypress/helpers). To understand the use of each directory check out [Folder Structure](https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests.html#Folder-Structure)
|
||||
|
||||
[helpers](../../console/cypress/helpers) directory is used for sharing reusable functions/constants across tests. Before adding a resubale function in this directory, consider if it will be better as a custom cypress command, if so, then add it to [Support](../../console/cypress/support) directory following this [guide](https://docs.cypress.io/api/cypress-api/custom-commands.html), preferrably to [command.ts](../../console/cypress/support/commands.ts) file.
|
||||
|
||||
- Adding a Test
|
||||
|
||||
Tests go to [integration](../../console/cypress/integration) directory, where there are folders corresponding to Components in [Services](../../console/src/components/Services) directory (The top-level routes on the console).
|
||||
|
||||
Each of these folders contains different test folders, named after the particular feature they are testing. For example [create-table](../../console/cypress/integration/data/create-table) folder tests the functionality of creating a table from the console UI.
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { getElementFromAlias } from '../../helpers/dataHelpers';
|
||||
|
||||
export const viewOnboarding = () => {
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('onboarding-popup'))
|
||||
.should('be.visible')
|
||||
.should('contain.text', `Hi there, let's get started with Hasura!`);
|
||||
// cy.get(getElementFromAlias('btn-hide-for-now')).click();
|
||||
};
|
||||
export const hideNow = () => {
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('btn-hide-for-now')).click();
|
||||
cy.get(getElementFromAlias('onboarding-popup')).should('not.exist');
|
||||
};
|
||||
|
||||
export const dontShowAgain = () => {
|
||||
// Click on create
|
||||
cy.reload();
|
||||
cy.get(getElementFromAlias('onboarding-popup')).should('be.visible');
|
||||
|
||||
cy.get(getElementFromAlias('btn-ob-dont-show-again')).click();
|
||||
cy.get(getElementFromAlias('onboarding-popup')).should('not.exist');
|
||||
cy.reload();
|
||||
cy.get(getElementFromAlias('onboarding-popup')).should('not.exist');
|
||||
};
|
@ -1,39 +0,0 @@
|
||||
import { viewOnboarding, hideNow, dontShowAgain } from './spec';
|
||||
import { testMode } from '../../helpers/common';
|
||||
import { setMetaData } from '../validators/validators';
|
||||
import { getIndexRoute } from '../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
cy.visit(getIndexRoute());
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runActionsTests = () => {
|
||||
describe('onboarding', () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
it.skip('should show onboarding guide', viewOnboarding);
|
||||
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
it.skip('should hide when user click on Hide Now', hideNow);
|
||||
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
it.skip(
|
||||
'should hide forever when user click on Dont Show again',
|
||||
dontShowAgain
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runActionsTests();
|
||||
}
|
@ -1,164 +0,0 @@
|
||||
import { testMode } from '../../../helpers/common';
|
||||
|
||||
import { logMetadataRequests } from './utils/requests/logMetadataRequests';
|
||||
import { addNumbersActionMustNotExist } from './utils/testState/addNumbersActionMustNotExist';
|
||||
|
||||
// NOTE: This test suite does not include cases for relationships, headers and the codegen part
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Query Actions', () => {
|
||||
before(() => {
|
||||
addNumbersActionMustNotExist();
|
||||
logMetadataRequests();
|
||||
|
||||
cy.visit('/actions/manage/actions');
|
||||
});
|
||||
|
||||
after(() => {
|
||||
// Cleanup after the whole test file run
|
||||
|
||||
// Ensure the application is not there when manually deleting the created action to avoid any
|
||||
// potential client-side error that makes the test fail
|
||||
cy.visitEmptyPage();
|
||||
|
||||
// Delete the created action, if any
|
||||
addNumbersActionMustNotExist();
|
||||
});
|
||||
|
||||
it('When the users create, edit, and delete a Query Action, everything should work', () => {
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Step 1: Query Action creation**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Click on the Create button of the Actions panel**');
|
||||
cy.getBySel('data-create-actions').click();
|
||||
|
||||
// --------------------
|
||||
// Assign an alias to the most unclear selectors for future references
|
||||
cy.get('textarea').eq(0).as('actionDefinitionTextarea');
|
||||
cy.get('textarea').eq(1).as('typeConfigurationTextarea');
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Type in the Action Definition textarea**');
|
||||
cy.get('@actionDefinitionTextarea')
|
||||
.clearConsoleTextarea()
|
||||
.type(
|
||||
`type Query {
|
||||
addNumbers (numbers: [Int]): AddResult
|
||||
}`,
|
||||
{ force: true, delay: 0 }
|
||||
);
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Type in the Type Configuration textarea**');
|
||||
cy.get('@typeConfigurationTextarea')
|
||||
.clearConsoleTextarea()
|
||||
.type(
|
||||
`type AddResult {
|
||||
sum: Int
|
||||
}`,
|
||||
{ force: true, delay: 0 }
|
||||
);
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Type in the Webhook Handler field**');
|
||||
cy.getBySel('action-create-handler-input')
|
||||
.clearConsoleTextarea()
|
||||
.type('https://hasura-actions-demo.glitch.me/addNumbers', {
|
||||
delay: 0,
|
||||
parseSpecialCharSequences: false,
|
||||
});
|
||||
|
||||
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Click the Create button**');
|
||||
cy.getBySel('create-action-btn').click();
|
||||
|
||||
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Check if the success notification is visible**');
|
||||
cy.expectSuccessNotificationWithTitle('Created action successfully');
|
||||
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Step 2: Permission add and Handler change**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
// --------------------
|
||||
|
||||
cy.log('**--- Go the the action page**');
|
||||
cy.getBySel('actions-table-links').within(() => {
|
||||
cy.getBySel('addNumbers').click();
|
||||
});
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Type in the Webhook Handler field**');
|
||||
cy.getBySel('action-create-handler-input')
|
||||
.clearConsoleTextarea()
|
||||
.type('http://host.docker.internal:3000', {
|
||||
delay: 0,
|
||||
// parseSpecialCharSequences: false,
|
||||
});
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Click on the Save button**');
|
||||
cy.getBySel('save-modify-action-changes').click();
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Click the Permissions tab**');
|
||||
cy.getBySel('actions-permissions').click();
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Enter a new role**');
|
||||
cy.getBySel('role-textbox').type('manager');
|
||||
cy.getBySel('manager-Permission').click();
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Click Save Permissions**');
|
||||
cy.getBySel('save-permissions-for-action').click();
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Check if the success notification is visible**');
|
||||
cy.expectSuccessNotificationWithTitle('Permission saved successfully');
|
||||
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Step 3: Query Action delete**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Go the the action page**');
|
||||
cy.getBySel('actions-table-links').within(() => {
|
||||
cy.getBySel('addNumbers').click();
|
||||
});
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Set the prompt value**');
|
||||
cy.window().then(win => cy.stub(win, 'prompt').returns('addNumbers'));
|
||||
|
||||
cy.log('**--- Click the Delete button**');
|
||||
cy.getBySel('delete-action').click();
|
||||
|
||||
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Check if the success notification is visible**');
|
||||
cy.expectSuccessNotificationWithTitle('Action deleted successfully');
|
||||
});
|
||||
});
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
interface SingleMetadataRequest {
|
||||
type: string;
|
||||
// There are a lot of other fields, but tracking them is not important for the purpose of this module
|
||||
}
|
||||
|
||||
interface BulkMetadataRequest {
|
||||
type: 'bulk';
|
||||
args: SingleMetadataRequest[];
|
||||
}
|
||||
|
||||
type MetadataRequest = SingleMetadataRequest | BulkMetadataRequest;
|
||||
|
||||
/*
|
||||
* Log all the requests outgoing to the Metadata endpoint.
|
||||
* This is useful to have a glance of the requests that are going to the server.
|
||||
*/
|
||||
export function logMetadataRequests() {
|
||||
cy.intercept('POST', 'http://localhost:8080/v1/metadata', req => {
|
||||
const noArgs = !req.body.args;
|
||||
|
||||
if (noArgs) return;
|
||||
|
||||
const requestBody = req.body as MetadataRequest;
|
||||
|
||||
if (requestBody.type === 'bulk' || requestBody.type === 'concurrent_bulk') {
|
||||
const request = requestBody as BulkMetadataRequest;
|
||||
Cypress.log({ message: '*--- Bulk request*' });
|
||||
|
||||
request.args.forEach(arg =>
|
||||
Cypress.log({ message: `*--- Request: ${arg.type}*` })
|
||||
);
|
||||
} else {
|
||||
Cypress.log({ message: `*--- Request: ${requestBody.type}*` });
|
||||
}
|
||||
});
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
/**
|
||||
* Wait for a bunch of requests to be settled before proceeding with the test.
|
||||
*
|
||||
* Alternatively, https://github.com/bahmutov/cypress-network-idle could be used
|
||||
*
|
||||
* This is a workaround for "element is 'detached' from the DOM" Cypress' error (see the issue
|
||||
* linked below). Since the UI gets re-rendered because of the requests, this utility ensures that
|
||||
* all the requests parallelly made by the UI are settled before proceeding with the test. Hance, it
|
||||
* ensure the UI won't re-render during the next interaction.
|
||||
*
|
||||
* What are the requests that must be awaited? By looking at the Cypress Test Runner, they are the
|
||||
* following, made parallelly or in a rapid series.
|
||||
* 1. export_metadata
|
||||
* 2. export_metadata
|
||||
* 3. export_metadata
|
||||
* 4. test_webhook_transform
|
||||
* 5. test_webhook_transform
|
||||
* 6. test_webhook_transform
|
||||
* 7. test_webhook_transform
|
||||
* At the moment of writing, I'm not sure the number of requests are fixed or not. If they are fixed,
|
||||
* using the cy.intercept `times` options would result in a more expressive and less convoluted code.
|
||||
*
|
||||
* To give you an overall idea, this is a timeline of the requests
|
||||
*
|
||||
* all requests start all requests end
|
||||
* | | | |
|
||||
* |--🚦🔴--1--2--3--4--5--6--7----------------------------1--2--3--4--5--6-7--🚦🟢--|
|
||||
*
|
||||
*
|
||||
* ATTENTION: Despite the defensive approach and the flakiness-removal purpose, this function could
|
||||
* introduced even more flakiness because of its empiric approach. In case of failures, it must be
|
||||
* carefully evaluated when/if keeping it or thinking about a better approach.
|
||||
* In generale, this solution does not scale, at should not be spread among the tests.
|
||||
*
|
||||
* @see https://github.com/cypress-io/cypress/issues/7306
|
||||
* @see https://glebbahmutov.com/blog/detached/
|
||||
* @see https://github.com/bahmutov/cypress-network-idle
|
||||
*/
|
||||
|
||||
import 'cypress-wait-until';
|
||||
|
||||
export function waitForPostCreationRequests() {
|
||||
let waitCompleted = false;
|
||||
|
||||
cy.log('*--- All requests must be settled*');
|
||||
|
||||
const pendingRequests = new Map();
|
||||
cy.intercept('POST', 'http://localhost:8080/v1/metadata', req => {
|
||||
if (waitCompleted) return;
|
||||
|
||||
Cypress.log({ message: '*--- Request pending*' });
|
||||
|
||||
pendingRequests.set(req, true);
|
||||
|
||||
req.continue(() => {
|
||||
Cypress.log({ message: '*--- Request settled*' });
|
||||
|
||||
pendingRequests.delete(req);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.log({ message: '*--- Waiting for the first request to start*' });
|
||||
|
||||
// Check if at least one request has been caught. This check must protect from the following case
|
||||
//
|
||||
// check requests start test failure, the requests got the UI re-rendered
|
||||
// | | |
|
||||
// |--🚦🔴----⚠️---🚦🟢-------1-2-3-4-5-6-7-1----------💥
|
||||
//
|
||||
// where checking that "there are no pending requests" falls in the false positive case where
|
||||
// there are no pending requests because no one started at all.
|
||||
//
|
||||
// The check runs every millisecond to be 100% sure that no request can escape (ex. because of a
|
||||
// super fast server). A false-negative case represented here
|
||||
//
|
||||
// requests start requests end check check test failure, no first request caught
|
||||
// | | | | | | |
|
||||
// |--🚦🔴--1-2-3-4-5-6-7-1-2-3-4-5-6-7--⚠️------------------⚠️------------------💥
|
||||
cy.waitUntil(() => pendingRequests.size > 0, {
|
||||
timeout: 5000, // 5 seconds is the default Cypress wait for a request to start
|
||||
interval: 1,
|
||||
errorMsg: 'No first request caught',
|
||||
});
|
||||
|
||||
Cypress.log({ message: '*--- Waiting for all the requests to start*' });
|
||||
|
||||
// Let pass some time to collect all the requests. Otherwise, it could detect that the first
|
||||
// request complete and go on with the test, even if another one will be performed in a while.
|
||||
//
|
||||
// This fixed wait protects from the following timeline
|
||||
//
|
||||
// 1st request start first request end other requests start test failure, the requests got the UI re-rendered
|
||||
// | | | |
|
||||
// |--🚦🔴---1---------------------1----🚦🟢----------------2-3-4-5-6-7-1----------💥
|
||||
//
|
||||
// Obviously, it is an empiric waiting, that also slows down the test.
|
||||
cy.wait(500);
|
||||
|
||||
Cypress.log({ message: '*--- Waiting for all the requests to be settled*' });
|
||||
|
||||
cy.waitUntil(() => pendingRequests.size === 0, {
|
||||
timeout: 30000, // 30 seconds is the default Cypress wait for the request to complete
|
||||
errorMsg: 'Some requests are not settled yet',
|
||||
}).then(() => {
|
||||
waitCompleted = true;
|
||||
});
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Delete the Action straight from the server.
|
||||
*/
|
||||
export function deleteAddNumbersAction() {
|
||||
Cypress.log({ message: '**--- Action delete: start**' });
|
||||
|
||||
return cy
|
||||
.request('POST', 'http://localhost:8080/v1/metadata', {
|
||||
type: 'drop_action',
|
||||
args: { name: 'addNumbers' },
|
||||
})
|
||||
.then(() => Cypress.log({ message: '**--- Action delete: end**' }));
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Read the Metadata straight from the server.
|
||||
*/
|
||||
export function readMetadata() {
|
||||
Cypress.log({ message: '**--- Metadata read: start**' });
|
||||
|
||||
return cy
|
||||
.request('POST', 'http://localhost:8080/v1/metadata', {
|
||||
args: {},
|
||||
type: 'export_metadata',
|
||||
})
|
||||
.then(_response => {
|
||||
Cypress.log({ message: '**--- Metadata read: end**' });
|
||||
});
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
import { readMetadata } from '../services/readMetadata';
|
||||
import { deleteAddNumbersAction } from '../services/deleteAddNumbersAction';
|
||||
|
||||
/**
|
||||
* Ensure the Action does not exist.
|
||||
*/
|
||||
export function addNumbersActionMustNotExist() {
|
||||
Cypress.log({ message: '**--- Action check: start**' });
|
||||
|
||||
readMetadata().then(response => {
|
||||
const actionExists = !!response.body.actions?.find(
|
||||
// TODO: properly type it
|
||||
action => action.name === 'addNumbers'
|
||||
);
|
||||
|
||||
if (actionExists) {
|
||||
Cypress.log({ message: '**--- The Action must be deleted**' });
|
||||
deleteAddNumbersAction();
|
||||
}
|
||||
});
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
// const setup = () => {
|
||||
// describe.skip('Setup route', () => {
|
||||
// it('Visit the index route', () => {
|
||||
// cy.visit('/actions/manage/actions');
|
||||
// // Get and set validation metadata
|
||||
// setMetaData();
|
||||
// });
|
||||
// });
|
||||
// };
|
||||
|
||||
// TODO: what about the codegen part? Why is it not tested?
|
||||
|
||||
// export const runActionsTests = () => {
|
||||
// describe.skip('Actions', () => {
|
||||
// The test has been moved to mutationAction.e2e.test
|
||||
// it('Create Mutation Action', createMutationAction);
|
||||
|
||||
// The test was commented before moving the other ones to mutationAction.e2e.test
|
||||
// it('Verify Mutation Actions on GraphiQL', verifyMutation);
|
||||
|
||||
// The test has been moved to mutationAction.e2e.test
|
||||
// it('Modify Mutation Action', modifyMutationAction);
|
||||
|
||||
// The test has been moved to mutationAction.e2e.test
|
||||
// it('Delete Mutation Action', deleteMutationAction);
|
||||
|
||||
// The test has been moved to queryAction.e2e.test.e2e.test
|
||||
// it('Create Query Action', createQueryAction);
|
||||
|
||||
// The test was commented before moving the other ones to queryAction.e2e.test
|
||||
// it('Verify Query Actions on GraphiQL', verifyQuery);
|
||||
|
||||
// The test has been moved to queryAction.e2e.test.e2e.test
|
||||
// it('Modify Query Action', modifyQueryAction);
|
||||
|
||||
// The test has been moved to queryAction.e2e.test.e2e.test
|
||||
// it('Delete Query Action', deleteQueryAction);
|
||||
|
||||
// The test has been moved to actionWithTransform.e2e.test.ts
|
||||
// it('Create Action With Transform', createActionTransform);
|
||||
|
||||
// The test has been moved to actionWithTransform.e2e.test.ts
|
||||
// it('Update Action With Transform', modifyActionTransform);
|
||||
|
||||
// The test has been moved to actionWithTransform.e2e.test.ts
|
||||
// it('Delete Action With Transform', deleteActionTransform);
|
||||
|
||||
// The test has been moved to v1ActionWithTransform.e2e.test.ts
|
||||
// it(
|
||||
// 'Create an action with V1 Transform and edit it through console, which will lead to the action being saved as V2',
|
||||
// modifyV1ActionTransform
|
||||
// );
|
||||
// });
|
||||
// };
|
||||
|
||||
// if (testMode !== 'cli') {
|
||||
// setup();
|
||||
// runActionsTests();
|
||||
// }
|
@ -1,350 +0,0 @@
|
||||
import { testMode } from '../../../helpers/common';
|
||||
|
||||
import { logMetadataRequests } from './utils/requests/logMetadataRequests';
|
||||
import { readMetadata } from './utils/services/readMetadata';
|
||||
import { loginActionMustNotExist } from './utils/testState/loginActionMustNotExist';
|
||||
import { Metadata } from '../../../../src/features/hasura-metadata-types';
|
||||
import { checkMetadataPayload } from '../../utils/checkMetadataPayload';
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
describe('Actions with Transform', () => {
|
||||
before(() => {
|
||||
loginActionMustNotExist();
|
||||
logMetadataRequests();
|
||||
|
||||
cy.visit('/actions/manage/actions');
|
||||
});
|
||||
|
||||
after(() => {
|
||||
// Delete the created action, if any
|
||||
loginActionMustNotExist();
|
||||
});
|
||||
|
||||
it('When the users create, and delete a Action with Transform, everything should work', () => {
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Step 1: Action with Transform creation**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Click on the Create button of the Actions panel**');
|
||||
cy.getBySel('data-create-actions').click();
|
||||
|
||||
// Assign an alias to the most unclear selectors for future references
|
||||
cy.get('textarea').eq(0).as('actionDefinitionTextarea');
|
||||
cy.get('textarea').eq(1).as('typeConfigurationTextarea');
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Type in the Action Definition textarea**');
|
||||
cy.get('@actionDefinitionTextarea')
|
||||
.clearConsoleTextarea()
|
||||
.type(
|
||||
`type Mutation {
|
||||
login (username: String!, password: String!): LoginResponse
|
||||
}`,
|
||||
{ force: true, delay: 0 }
|
||||
);
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Type in the Type Configuration textarea**');
|
||||
cy.get('@typeConfigurationTextarea')
|
||||
.clearConsoleTextarea()
|
||||
.type(
|
||||
`type LoginResponse {
|
||||
accessToken: String!
|
||||
}`,
|
||||
{ force: true, delay: 0 }
|
||||
);
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Click the Add Request Options Transform button**');
|
||||
cy.contains('Add Request Options Transform').click();
|
||||
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Step 1.1: Add URL**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
cy.get('[data-cy="Change Request Options"]').within(() => {
|
||||
// --------------------
|
||||
cy.log('**--- Choose POST**');
|
||||
cy.contains('POST').click();
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Type in the Request URL Template field**');
|
||||
cy.get('[placeholder="URL Template (Optional)..."]').type('/users');
|
||||
});
|
||||
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Step 1.2: Add Env Var**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Type in the Webhook Handler field**');
|
||||
cy.getBySel('action-create-handler-input')
|
||||
.clearConsoleTextarea()
|
||||
.type('{{MY_WEBHOOK}}', {
|
||||
delay: 0,
|
||||
parseSpecialCharSequences: false,
|
||||
});
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Click the Show Sample Context button**');
|
||||
cy.contains('Show Sample Context').click();
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Type in the Env Variables Key field**');
|
||||
cy.getBySel('transform-env-vars-kv-key-0').type('MY_WEBHOOK', {
|
||||
delay: 1,
|
||||
});
|
||||
cy.log('**--- Type in the Env Variables Value field**');
|
||||
cy.getBySel('transform-env-vars-kv-value-0').type('https://handler.com', {
|
||||
delay: 1,
|
||||
});
|
||||
|
||||
// --------------------
|
||||
cy.get('[data-cy="Change Request Options"]').within(() => {
|
||||
cy.log('**--- Check the Preview of the Request URL Template**');
|
||||
cy.getBySel('transform-requestUrl-preview').should(
|
||||
'have.value',
|
||||
'https://handler.com/users'
|
||||
);
|
||||
});
|
||||
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Step 1.3: Add path**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Type in the Webhook Handler field**');
|
||||
cy.getBySel('action-create-handler-input')
|
||||
.clearConsoleTextarea()
|
||||
.type('https://hasura-actions-demo.glitch.me', {
|
||||
delay: 0,
|
||||
parseSpecialCharSequences: false,
|
||||
});
|
||||
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Step 1.4: Query Params add**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
cy.get('[placeholder="URL Template (Optional)..."]')
|
||||
.clearConsoleTextarea()
|
||||
.type('/{{$body.action.name}}', { parseSpecialCharSequences: false });
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Type in the first Query Params Key field**');
|
||||
cy.getBySel('transform-query-params-kv-key-0').type('id');
|
||||
cy.log('**--- Type in the first Query Params Value field**');
|
||||
cy.getBySel('transform-query-params-kv-value-0').type('5');
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Type in the second Query Params Key field**');
|
||||
cy.getBySel('transform-query-params-kv-key-1').type('name');
|
||||
cy.log('**--- Type in the second Query Params Value field**');
|
||||
cy.getBySel('transform-query-params-kv-value-1').type(
|
||||
'{{$body.action.name}}',
|
||||
{
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 0,
|
||||
}
|
||||
);
|
||||
|
||||
// --------------------
|
||||
cy.get('[data-cy="Change Request Options"]').within(() => {
|
||||
cy.log('**--- Check the Preview of the Request URL Template**');
|
||||
cy.findByLabelText('Preview').should(
|
||||
'have.value',
|
||||
'https://hasura-actions-demo.glitch.me/login?name=login&id=5'
|
||||
);
|
||||
});
|
||||
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Step 1.5: Add Payload Transform**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Click the Add Payload Transform button**');
|
||||
cy.contains('Add Payload Transform').click();
|
||||
|
||||
// --------------------
|
||||
cy.get('[data-cy="Change Payload"]').within(() => {
|
||||
// Assign an alias to the most unclear selectors for future references
|
||||
cy.get('textarea').eq(1).as('payloadTransformRequestBody');
|
||||
|
||||
cy.log('**--- Type in the Payload Transform Request Body textarea**');
|
||||
cy.get('@payloadTransformRequestBody')
|
||||
.wait(500)
|
||||
.clearConsoleTextarea()
|
||||
.wait(1000) // Work around the fact that this test fails in CI but not locally
|
||||
.clearConsoleTextarea()
|
||||
.wait(1000) // Work around the fact that this test fails in CI but not locally
|
||||
.type(
|
||||
`{
|
||||
"userInfo": {
|
||||
"name": {{$body.input.username}},
|
||||
"password": {{$body.input.password}},
|
||||
"type": {{$body.action.name}}
|
||||
`,
|
||||
// delay is set to 1 because setting it to 0 causes the test to fail because writes
|
||||
// something like
|
||||
// "name": {{$body.input.username}}name
|
||||
// in the textarea (the closing "name" is a mistake)
|
||||
{ force: true, delay: 1, parseSpecialCharSequences: false }
|
||||
);
|
||||
});
|
||||
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Step 1.5: Add Response Transform**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Click the Add Response Transform button**');
|
||||
cy.contains('Add Response Transform').click({ force: true });
|
||||
|
||||
// --------------------
|
||||
cy.get('[data-cy="Change Response"]').within(() => {
|
||||
// Assign an alias to the most unclear selectors for future references
|
||||
cy.get('textarea').eq(0).as('responseTransformResponseBody');
|
||||
|
||||
cy.log('**--- Type in the Response Transform Response Body textarea**');
|
||||
cy.get('@responseTransformResponseBody')
|
||||
.wait(500)
|
||||
.clearConsoleTextarea()
|
||||
.wait(500)
|
||||
.clearConsoleTextarea()
|
||||
.wait(500)
|
||||
.type(
|
||||
`{
|
||||
"userInfo": {
|
||||
"name": {{$body.input.username}},
|
||||
"password": {{$body.input.password}},
|
||||
"type": {{$body.action.name}}
|
||||
`,
|
||||
// delay is set to 1 because setting it to 0 causes the test to fail because writes
|
||||
// something like
|
||||
// "name": {{$body.input.username}}name
|
||||
// in the textarea (the closing "name" is a mistake)
|
||||
{ force: true, delay: 1, parseSpecialCharSequences: false }
|
||||
);
|
||||
});
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Click the Create button**');
|
||||
// cy.wait(1000) because of debounce
|
||||
|
||||
cy.intercept('POST', 'http://localhost:8080/v1/metadata', req => {
|
||||
if (JSON.stringify(req.body).includes('create_action')) {
|
||||
req.alias = 'createAction';
|
||||
}
|
||||
req.continue();
|
||||
});
|
||||
|
||||
cy.intercept('POST', 'http://localhost:9693/apis/migrate', req => {
|
||||
if (JSON.stringify(req.body).includes('create_action')) {
|
||||
req.alias = 'createAction';
|
||||
}
|
||||
});
|
||||
|
||||
cy.wait(1000);
|
||||
|
||||
cy.getBySel('create-action-btn').click();
|
||||
|
||||
cy.wait('@createAction').then(interception => {
|
||||
checkMetadataPayload(interception, { name: 'Action payload' });
|
||||
});
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Check if the success notification is visible**');
|
||||
cy.expectSuccessNotificationWithTitle('Created action successfully');
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// The "Action change" part has been removed since it caused Cypress to crash
|
||||
// TODO: identify the crashing reason
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// cy.log('**------------------------------**');
|
||||
// cy.log('**------------------------------**');
|
||||
// cy.log('**------------------------------**');
|
||||
// cy.log('**--- Step 2: Action change**');
|
||||
// cy.log('**------------------------------**');
|
||||
// cy.log('**------------------------------**');
|
||||
// cy.log('**------------------------------**');
|
||||
|
||||
readMetadata().then((md: { body: Metadata['metadata'] }) => {
|
||||
cy.wrap(
|
||||
(md.body.actions || []).find(action => action.name === 'login')
|
||||
).snapshot({
|
||||
name: 'Action metadata',
|
||||
});
|
||||
});
|
||||
|
||||
// // --------------------
|
||||
cy.log('**--- Wait all the requests to be settled**');
|
||||
|
||||
// cy.get('[data-cy="Change Request Options"]').within(() => {
|
||||
// // --------------------
|
||||
// cy.log('**--- Choose GET**');
|
||||
// cy.contains('GET').click();
|
||||
|
||||
// // --------------------
|
||||
// cy.log('**--- Type in the Request URL Template field**');
|
||||
|
||||
// cy.get('[placeholder="URL Template (Optional)..."]')
|
||||
// .clearConsoleTextarea()
|
||||
// .type('/{{$body.action.name}}/actions', {
|
||||
// delay: 0,
|
||||
// parseSpecialCharSequences: false,
|
||||
// });
|
||||
|
||||
// // --------------------
|
||||
// cy.log('**--- Click on the first Remove Query Param button**');
|
||||
// cy.getBySel('transform-query-params-kv-remove-button-0').click();
|
||||
// });
|
||||
|
||||
// // --------------------
|
||||
// cy.log('**--- Click the Remove Payload Transform button**');
|
||||
// cy.contains('Remove Payload Transform').click();
|
||||
|
||||
// // --------------------
|
||||
// cy.log('**--- Click on the Save button**');
|
||||
// cy.getBySel('save-modify-action-changes').click();
|
||||
|
||||
// // --------------------
|
||||
// cy.log('**--- Check if the success notification is visible**');
|
||||
// cy.expectSuccessNotificationWithTitle('Action saved successfully');
|
||||
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Step 3: Action delete**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Go the the action page**');
|
||||
cy.getBySel('actions-table-links').within(() => {
|
||||
cy.getBySel('login').click();
|
||||
});
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Set the prompt value**');
|
||||
cy.window().then(win => cy.stub(win, 'prompt').returns('login'));
|
||||
|
||||
cy.log('**--- Click the Delete button**');
|
||||
cy.getBySel('delete-action').click();
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Check the prompt has been called**');
|
||||
cy.window().its('prompt').should('be.called');
|
||||
|
||||
// --------------------
|
||||
cy.log('**--- Check if the success notification is visible**');
|
||||
cy.expectSuccessNotificationWithTitle('Action deleted successfully');
|
||||
});
|
||||
});
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0",
|
||||
"Actions with Transform": {
|
||||
"When the users create, and delete a Action with Transform, everything should work": {
|
||||
"Action payload": {
|
||||
"bodyToSnapshot": [
|
||||
{
|
||||
"type": "set_custom_types",
|
||||
"args": {
|
||||
"scalars": [],
|
||||
"input_objects": [],
|
||||
"objects": [
|
||||
{
|
||||
"name": "LoginResponse",
|
||||
"description": null,
|
||||
"fields": [
|
||||
{
|
||||
"name": "accessToken",
|
||||
"type": "String!",
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"enums": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "create_action",
|
||||
"args": {
|
||||
"name": "login",
|
||||
"definition": {
|
||||
"arguments": [
|
||||
{
|
||||
"name": "username",
|
||||
"type": "String!",
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"type": "String!",
|
||||
"description": null
|
||||
}
|
||||
],
|
||||
"kind": "synchronous",
|
||||
"output_type": "LoginResponse",
|
||||
"handler": "https://hasura-actions-demo.glitch.me",
|
||||
"type": "mutation",
|
||||
"headers": [],
|
||||
"forward_client_headers": false,
|
||||
"timeout": null,
|
||||
"request_transform": {
|
||||
"version": 2,
|
||||
"template_engine": "Kriti",
|
||||
"method": "POST",
|
||||
"url": "{{$base_url}}/{{$body.action.name}}",
|
||||
"query_params": {
|
||||
"id": "5",
|
||||
"name": "{{$body.action.name}}"
|
||||
},
|
||||
"body": {
|
||||
"action": "transform",
|
||||
"template": "{\n \"userInfo\": {\n \"name\": {{$body.input.username}},\n \"password\": {{$body.input.password}},\n \"type\": {{$body.action.name}}\n \n }\n}"
|
||||
}
|
||||
},
|
||||
"response_transform": {
|
||||
"version": 2,
|
||||
"body": {
|
||||
"action": "transform",
|
||||
"template": "{\n \"userInfo\": {\n \"name\": {{$body.input.username}},\n \"password\": {{$body.input.password}},\n \"type\": {{$body.action.name}}\n \n }\n}"
|
||||
},
|
||||
"template_engine": "Kriti"
|
||||
}
|
||||
},
|
||||
"comment": null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Action metadata": {
|
||||
"name": "login",
|
||||
"definition": {
|
||||
"handler": "https://hasura-actions-demo.glitch.me",
|
||||
"output_type": "LoginResponse",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "username",
|
||||
"type": "String!"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"type": "String!"
|
||||
}
|
||||
],
|
||||
"request_transform": {
|
||||
"body": {
|
||||
"action": "transform",
|
||||
"template": "{\n \"userInfo\": {\n \"name\": {{$body.input.username}},\n \"password\": {{$body.input.password}},\n \"type\": {{$body.action.name}}\n \n }\n}"
|
||||
},
|
||||
"method": "POST",
|
||||
"query_params": {
|
||||
"id": "5",
|
||||
"name": "{{$body.action.name}}"
|
||||
},
|
||||
"template_engine": "Kriti",
|
||||
"url": "{{$base_url}}/{{$body.action.name}}",
|
||||
"version": 2
|
||||
},
|
||||
"response_transform": {
|
||||
"body": {
|
||||
"action": "transform",
|
||||
"template": "{\n \"userInfo\": {\n \"name\": {{$body.input.username}},\n \"password\": {{$body.input.password}},\n \"type\": {{$body.action.name}}\n \n }\n}"
|
||||
},
|
||||
"template_engine": "Kriti",
|
||||
"version": 2
|
||||
},
|
||||
"type": "mutation",
|
||||
"kind": "synchronous"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
interface SingleMetadataRequest {
|
||||
type: string;
|
||||
// There are a lot of other fields, but tracking them is not important for the purpose of this module
|
||||
}
|
||||
|
||||
interface BulkMetadataRequest {
|
||||
type: 'bulk';
|
||||
args: SingleMetadataRequest[];
|
||||
}
|
||||
|
||||
type MetadataRequest = SingleMetadataRequest | BulkMetadataRequest;
|
||||
|
||||
/*
|
||||
* Log all the requests outgoing to the Metadata endpoint.
|
||||
* This is useful to have a glance of the requests that are going to the server.
|
||||
*/
|
||||
export function logMetadataRequests() {
|
||||
cy.intercept('POST', 'http://localhost:8080/v1/metadata', req => {
|
||||
const noArgs = !req.body.args;
|
||||
|
||||
if (noArgs) return;
|
||||
|
||||
const requestBody = req.body as MetadataRequest;
|
||||
|
||||
if (requestBody.type === 'bulk' || requestBody.type === 'concurrent_bulk') {
|
||||
const request = requestBody as BulkMetadataRequest;
|
||||
Cypress.log({ message: '*--- Bulk request*' });
|
||||
|
||||
request.args.forEach(arg =>
|
||||
Cypress.log({ message: `*--- Request: ${arg.type}*` })
|
||||
);
|
||||
} else {
|
||||
Cypress.log({ message: `*--- Request: ${requestBody.type}*` });
|
||||
}
|
||||
});
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Delete the Action straight from the server.
|
||||
*/
|
||||
export function deleteLoginAction() {
|
||||
Cypress.log({ message: '**--- Action delete: start**' });
|
||||
|
||||
return cy
|
||||
.request('POST', 'http://localhost:8080/v1/metadata', {
|
||||
type: 'drop_action',
|
||||
args: { name: 'login' },
|
||||
})
|
||||
.then(() => Cypress.log({ message: '**--- Action delete: end**' }));
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Delete the Action straight from the server.
|
||||
*/
|
||||
export function deleteV1LoginAction() {
|
||||
Cypress.log({ message: '**--- Action delete: start**' });
|
||||
|
||||
return cy
|
||||
.request('POST', 'http://localhost:8080/v1/metadata', {
|
||||
type: 'drop_action',
|
||||
args: { name: 'v1Login' },
|
||||
})
|
||||
.then(() => Cypress.log({ message: '**--- Action delete: end**' }));
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Read the Metadata straight from the server.
|
||||
*/
|
||||
export function readMetadata() {
|
||||
Cypress.log({ message: '**--- Metadata read: start**' });
|
||||
|
||||
return cy
|
||||
.request('POST', 'http://localhost:8080/v1/metadata', {
|
||||
args: {},
|
||||
type: 'export_metadata',
|
||||
})
|
||||
.then(_response => {
|
||||
Cypress.log({ message: '**--- Metadata read: end**' });
|
||||
});
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
import { readMetadata } from '../services/readMetadata';
|
||||
import { deleteLoginAction } from '../services/deleteLoginAction';
|
||||
|
||||
/**
|
||||
* Ensure the Action does not exist.
|
||||
*/
|
||||
export function loginActionMustNotExist() {
|
||||
Cypress.log({ message: '**--- Action check: start**' });
|
||||
|
||||
readMetadata().then(response => {
|
||||
const actionExists = !!response.body.actions?.find(
|
||||
// TODO: properly type it
|
||||
action => action.name === 'login'
|
||||
);
|
||||
|
||||
if (actionExists) {
|
||||
Cypress.log({ message: '**--- The Action must be deleted**' });
|
||||
deleteLoginAction();
|
||||
}
|
||||
});
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
import {
|
||||
getElementFromAlias,
|
||||
baseUrl,
|
||||
tableColumnTypeSelector,
|
||||
makeDataAPIOptions,
|
||||
getIndexRoute,
|
||||
} from '../../../helpers/dataHelpers';
|
||||
import { validateCT, ResultType } from '../../validators/validators';
|
||||
import { toggleOnMigrationMode } from '../../data/migration-mode/utils';
|
||||
import { setPromptValue } from '../../../helpers/common';
|
||||
// ***************** UTIL FUNCTIONS **************************
|
||||
|
||||
let adminSecret: string;
|
||||
let dataApiUrl: string;
|
||||
|
||||
export const createTestTable = () => {
|
||||
cy.window().then(win => {
|
||||
adminSecret = win.__env.adminSecret;
|
||||
dataApiUrl = win.__env.dataApiUrl;
|
||||
const { consoleMode } = win.__env;
|
||||
if (consoleMode === 'cli') {
|
||||
toggleOnMigrationMode();
|
||||
}
|
||||
});
|
||||
|
||||
// Click on the create table button
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(15000);
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
// Enter the table name
|
||||
cy.get(getElementFromAlias('tableName')).type('users');
|
||||
// Set first column
|
||||
cy.get(getElementFromAlias('column-0')).clear().type('id');
|
||||
tableColumnTypeSelector('col-type-0');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
|
||||
.first()
|
||||
.click();
|
||||
cy.get(getElementFromAlias('column-1')).clear().type('name');
|
||||
tableColumnTypeSelector('col-type-1');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_text'))
|
||||
.first()
|
||||
.click();
|
||||
|
||||
// Set primary key
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(10000);
|
||||
// Check if the table got created and navigatied to modify table
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/users/modify`
|
||||
);
|
||||
// Validate
|
||||
validateCT('users', ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const insertValue = () => {
|
||||
cy.get(getElementFromAlias('table-insert-rows')).click();
|
||||
// Insert a row
|
||||
cy.get(getElementFromAlias('typed-input-1')).type('someName');
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
};
|
||||
|
||||
export const openAPIExplorer = () => {
|
||||
// Open API Explorer
|
||||
cy.get(getElementFromAlias('api')).click();
|
||||
cy.wait(3000);
|
||||
};
|
||||
|
||||
export const checkQuery = () => {
|
||||
if (adminSecret) {
|
||||
cy.get(getElementFromAlias('header-key-2')).type('someKey');
|
||||
cy.get(getElementFromAlias('header-value-2')).type('someValue');
|
||||
} else {
|
||||
cy.get(getElementFromAlias('header-key-1')).type('someKey');
|
||||
cy.get(getElementFromAlias('header-value-1')).type('someValue');
|
||||
}
|
||||
|
||||
cy.get('textarea')
|
||||
.first()
|
||||
.type('{enter}{uparrow}query{{}users{{}id}}', { force: true });
|
||||
cy.wait(1000);
|
||||
cy.get('.execute-button').click();
|
||||
cy.get('.cm-property').contains('id');
|
||||
cy.get('.cm-number').contains('1');
|
||||
};
|
||||
|
||||
export const checkMutation = () => {
|
||||
cy.get('textarea')
|
||||
.first()
|
||||
.type(
|
||||
'{enter}{uparrow}#{leftarrow}{enter}{uparrow}mutation insert_user{{}insert_users(objects:[{{}name:"someName"}]){{}returning{{}id}}}',
|
||||
{ force: true }
|
||||
);
|
||||
cy.wait(1000);
|
||||
cy.get('.execute-button').click();
|
||||
cy.wait(5000);
|
||||
cy.get('.cm-property').contains('id');
|
||||
cy.get('.cm-number').contains('2');
|
||||
};
|
||||
|
||||
export const checkSub = () => {
|
||||
// Make a subscription
|
||||
cy.get('textarea')
|
||||
.first()
|
||||
.type(
|
||||
'{enter}{uparrow}#{leftarrow}{enter}{uparrow}subscription{{}users{{}name}}',
|
||||
{ force: true }
|
||||
);
|
||||
cy.wait(1000);
|
||||
cy.get('.execute-button').click();
|
||||
cy.wait(5000);
|
||||
cy.get('.cm-property').contains('name');
|
||||
cy.get('.cm-string').contains('someName');
|
||||
// Update the user with id 1
|
||||
const reqBody = {
|
||||
type: 'update',
|
||||
args: {
|
||||
table: {
|
||||
name: 'users',
|
||||
},
|
||||
where: {
|
||||
id: '1',
|
||||
},
|
||||
$set: {
|
||||
name: 'someOtherName',
|
||||
},
|
||||
},
|
||||
};
|
||||
// Make the request
|
||||
const requestOptions = makeDataAPIOptions(dataApiUrl, adminSecret, reqBody);
|
||||
cy.request(requestOptions).then(res => {
|
||||
cy.log(JSON.stringify(res));
|
||||
cy.wait(3000);
|
||||
cy.get('.cm-string').contains('someOtherName');
|
||||
});
|
||||
};
|
||||
|
||||
export const delTestTable = () => {
|
||||
cy.get(getElementFromAlias('data-tab-link')).click();
|
||||
// Go to the modify section of the table
|
||||
cy.get(getElementFromAlias('users')).click();
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
setPromptValue('users');
|
||||
// Click on delete
|
||||
cy.get(getElementFromAlias('delete-table')).click();
|
||||
// Confirm
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(5000);
|
||||
|
||||
// Temporarily disabled, until it's fixed on the main branch
|
||||
// Match the URL
|
||||
// cy.url().should('eq', `${baseUrl}/data/default/schema/public`);
|
||||
|
||||
// Validate
|
||||
validateCT('users', ResultType.FAILURE);
|
||||
};
|
@ -1,44 +0,0 @@
|
||||
import {
|
||||
openAPIExplorer,
|
||||
checkQuery,
|
||||
checkMutation,
|
||||
createTestTable,
|
||||
insertValue,
|
||||
checkSub,
|
||||
delTestTable,
|
||||
} from './spec';
|
||||
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { testMode } from '../../../helpers/common';
|
||||
|
||||
const setup = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
// Visit the index route
|
||||
cy.visit('/');
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runApiExplorerTests = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('API Explorer', () => {
|
||||
it('Create test table', createTestTable);
|
||||
it('Insert row into test table', insertValue);
|
||||
it('Open API Explorer', openAPIExplorer);
|
||||
it('Check query result', checkQuery);
|
||||
it('Check mutation result', checkMutation);
|
||||
it('Check subscription result', checkSub);
|
||||
it('Delete test table', delTestTable);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runApiExplorerTests();
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import { testMode } from '../../../helpers/common';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
describe('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
// Visit the index route
|
||||
cy.visit(getIndexRoute());
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const run404Test = () => {
|
||||
describe('404', () => {
|
||||
it('Open random page', () => {
|
||||
cy.visit('/someRandomPage');
|
||||
cy.get('h1').contains('404');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
run404Test();
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
import {
|
||||
baseUrl,
|
||||
getElementFromAlias,
|
||||
getElementFromClassName,
|
||||
} from '../../../helpers/dataHelpers';
|
||||
|
||||
const statements = {
|
||||
createTableSql:
|
||||
'CREATE TABLE a_test_test_author (id serial PRIMARY KEY, first_name text, last_name text);',
|
||||
createCustomFuncSql: `CREATE OR REPLACE FUNCTION test_get_author_full_name(a_test_test_author_row a_test_test_author)
|
||||
RETURNS TEXT AS $function$
|
||||
SELECT a_test_test_author_row.first_name || ' ' || a_test_test_author_row.last_name
|
||||
$function$
|
||||
LANGUAGE sql STABLE;`,
|
||||
insertData_a1: `INSERT INTO a_test_test_author(first_name, last_name) VALUES ('ruskin', 'bond');`,
|
||||
insertData_a2: `INSERT INTO a_test_test_author(first_name, last_name) VALUES ('enid', 'blyton');`,
|
||||
cleanUpSql: 'DROP TABLE a_test_test_author CASCADE;',
|
||||
graphql: {
|
||||
query: `{
|
||||
a_test_test_author {
|
||||
full_name # this is the computed field`,
|
||||
},
|
||||
};
|
||||
|
||||
export const openRawSQL = () => {
|
||||
cy.get('a').contains('Data').click();
|
||||
cy.wait(3000);
|
||||
cy.get(getElementFromAlias('sql-link')).click();
|
||||
cy.wait(3000);
|
||||
cy.url().should('eq', `${baseUrl}/data/sql`);
|
||||
};
|
||||
|
||||
const clearText = () => {
|
||||
cy.get('textarea').type('{selectall}', { force: true });
|
||||
cy.get('textarea').trigger('keydown', {
|
||||
keyCode: 46,
|
||||
which: 46,
|
||||
force: true,
|
||||
});
|
||||
cy.wait(2000);
|
||||
};
|
||||
|
||||
// helper to type into the SQL textarea on rawsql page
|
||||
const typeStatement = (
|
||||
statement: string,
|
||||
shouldClearText = false,
|
||||
waitTimeUponType = 2000,
|
||||
endWaitTime = 5000,
|
||||
uncheckTrackCheckbox = false
|
||||
) => {
|
||||
if (shouldClearText) {
|
||||
clearText();
|
||||
}
|
||||
cy.get('textarea').type(statement, { force: true });
|
||||
cy.wait(waitTimeUponType);
|
||||
if (uncheckTrackCheckbox) {
|
||||
cy.get(getElementFromAlias('raw-sql-track-check')).uncheck();
|
||||
}
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
// FIXME: maybe necessary for CLI mode
|
||||
// cy.get(getElementFromAlias('raw-sql-statement-timeout')).should('be.disabled');
|
||||
cy.wait(endWaitTime);
|
||||
};
|
||||
|
||||
export const createTableAuthor = () => typeStatement(statements.createTableSql);
|
||||
|
||||
export const createCustomFunction = () =>
|
||||
typeStatement(statements.createCustomFuncSql, true, 2000, 5000, true);
|
||||
|
||||
export const insertAuthorsIntoTable = () => {
|
||||
typeStatement(statements.insertData_a1, true);
|
||||
typeStatement(statements.insertData_a2, true);
|
||||
clearText();
|
||||
};
|
||||
|
||||
export const searchForTable = () => {
|
||||
// ADD LATER: after search functionality is implemented
|
||||
// cy.get(getElementFromAlias('search-tables')).type('a_test_test_author');
|
||||
// cy.get(getElementFromAlias('table-links')).should(
|
||||
// 'contain',
|
||||
// 'a_test_test_author'
|
||||
// );
|
||||
cy.get(getElementFromAlias('a_test_test_author')).click();
|
||||
};
|
||||
|
||||
export const openModifySection = () => {
|
||||
// open modify section
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
// click on computed field section
|
||||
cy.get(getElementFromAlias('modify-table-edit-computed-field-0')).click();
|
||||
// type name
|
||||
cy.get(getElementFromAlias('computed-field-name-input')).type('{selectall}', {
|
||||
force: true,
|
||||
});
|
||||
cy.get(getElementFromAlias('computed-field-name-input')).trigger('keydown', {
|
||||
keyCode: 46,
|
||||
which: 46,
|
||||
force: true,
|
||||
});
|
||||
cy.get(getElementFromAlias('computed-field-name-input')).type('full_name', {
|
||||
force: true,
|
||||
delay: 10,
|
||||
});
|
||||
cy.wait(2000);
|
||||
// type & select function name
|
||||
cy.get(getElementFromClassName('function-name-select__control'))
|
||||
.children('div')
|
||||
.click({ multiple: true })
|
||||
.find('input')
|
||||
.focus()
|
||||
.type('test_get_author_full_name', { force: true })
|
||||
.get(getElementFromClassName('function-name-select__menu'))
|
||||
.first()
|
||||
.click();
|
||||
// enter comment
|
||||
cy.get(getElementFromAlias('computed-field-comment-input')).type(
|
||||
'this is a test comment',
|
||||
{ force: true }
|
||||
);
|
||||
// saving the computed field
|
||||
cy.get(getElementFromAlias('modify-table-computed-field-0-save')).click();
|
||||
// verify that a computed field exists
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('computed-field-full_name')).contains('full_name');
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const routeToGraphiql = () => {
|
||||
cy.visit('/api/api-explorer');
|
||||
cy.wait(7000);
|
||||
cy.url().should('eq', `${baseUrl}/api/api-explorer`);
|
||||
};
|
||||
|
||||
export const verifyComputedFieldsResult = () => {
|
||||
// type the query
|
||||
cy.get('textarea')
|
||||
.first()
|
||||
.type(`{enter}{uparrow}${statements.graphql.query}`, { force: true });
|
||||
cy.wait(2000);
|
||||
// execute the query
|
||||
cy.get('.execute-button').click();
|
||||
// verify if full_name is present
|
||||
cy.get('.cm-property').contains('full_name');
|
||||
cy.get('.cm-string').contains('ruskin bond');
|
||||
cy.wait(2000);
|
||||
};
|
||||
|
||||
export const cleanUpSql = () => typeStatement(statements.cleanUpSql, true);
|
||||
|
||||
export const routeToSQLPage = () => {
|
||||
cy.visit('/data/sql');
|
||||
cy.wait(7000);
|
||||
cy.url().should('eq', `${baseUrl}/data/sql`);
|
||||
};
|
@ -1,51 +0,0 @@
|
||||
import {
|
||||
openRawSQL,
|
||||
createTableAuthor,
|
||||
createCustomFunction,
|
||||
insertAuthorsIntoTable,
|
||||
searchForTable,
|
||||
cleanUpSql,
|
||||
openModifySection,
|
||||
routeToGraphiql,
|
||||
verifyComputedFieldsResult,
|
||||
routeToSQLPage,
|
||||
} from './spec';
|
||||
import { testMode } from '../../../helpers/common';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
describe('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
cy.visit(getIndexRoute());
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runComputedFieldTests = () => {
|
||||
describe('Computed Fields', () => {
|
||||
it('Open Raw SQL page', openRawSQL);
|
||||
it('Create test table', createTableAuthor);
|
||||
it('Run SQL for custom function', createCustomFunction);
|
||||
it('Insert authors into table', insertAuthorsIntoTable);
|
||||
it('Search for table', searchForTable);
|
||||
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore them
|
||||
it.skip('Open Modify page and add computed field', openModifySection);
|
||||
it.skip('Route to GraphiQL page', routeToGraphiql);
|
||||
it.skip(
|
||||
'Check computed field results on GraphiQL',
|
||||
verifyComputedFieldsResult
|
||||
);
|
||||
it.skip('Route to Raw SQL page', routeToSQLPage);
|
||||
it.skip('Test cleanup', cleanUpSql);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runComputedFieldTests();
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,246 +0,0 @@
|
||||
import {
|
||||
tableColumnTypeSelector,
|
||||
getElementFromAlias,
|
||||
getTableName,
|
||||
getColName,
|
||||
baseUrl,
|
||||
getIndexRoute,
|
||||
} from '../../../helpers/dataHelpers';
|
||||
import {
|
||||
setMetaData,
|
||||
validateCT,
|
||||
ResultType,
|
||||
} from '../../validators/validators';
|
||||
import { setPromptValue } from '../../../helpers/common';
|
||||
|
||||
const testName = 'ct';
|
||||
|
||||
export const checkCreateTableRoute = () => {
|
||||
// Click on the create table button
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(15000);
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
// Match the URL
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public/table/add`);
|
||||
};
|
||||
|
||||
export const failCTWithoutColumns = () => {
|
||||
// Type table name
|
||||
cy.get(getElementFromAlias('tableName')).type(getTableName(0, testName));
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
// Check if the route didn't change
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public/table/add`);
|
||||
// Validate
|
||||
validateCT(getTableName(0, testName), ResultType.FAILURE);
|
||||
};
|
||||
|
||||
export const failCTWithoutPK = () => {
|
||||
// Set first column
|
||||
cy.get(getElementFromAlias('column-0')).type(getColName(0));
|
||||
tableColumnTypeSelector('col-type-0');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
|
||||
.first()
|
||||
.click();
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
// Check if the route didn't change
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public/table/add`);
|
||||
// Validate
|
||||
validateCT(getTableName(0, testName), ResultType.FAILURE);
|
||||
};
|
||||
|
||||
export const failCTDuplicateColumns = () => {
|
||||
// Set second column
|
||||
cy.get(getElementFromAlias('column-1')).type(getColName(0));
|
||||
tableColumnTypeSelector('col-type-1');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
|
||||
.first()
|
||||
.click();
|
||||
// Set primary key
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
// Check for an alert
|
||||
cy.on('window:alert', str => {
|
||||
expect(
|
||||
str === `You have the following column names repeated: [${getColName(0)}]`
|
||||
).to.be.true;
|
||||
});
|
||||
// Check if the route didn't change
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public/table/add`);
|
||||
// Validate
|
||||
validateCT(getTableName(0, testName), ResultType.FAILURE);
|
||||
};
|
||||
|
||||
export const failCTWrongDefaultValue = () => {
|
||||
// Set second column
|
||||
cy.get(getElementFromAlias('column-1')).clear().type(getColName(1));
|
||||
tableColumnTypeSelector('col-type-1');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
|
||||
.first()
|
||||
.click();
|
||||
cy.get(getElementFromAlias('col-default-1')).type('qwerty');
|
||||
// Set primary key
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
// Check if the route didn't change
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public/table/add`);
|
||||
// Validate
|
||||
validateCT(getTableName(0, testName), ResultType.FAILURE);
|
||||
};
|
||||
|
||||
export const passCT = () => {
|
||||
cy.get(getElementFromAlias('frequently-used-columns')).first().should('exist');
|
||||
// Set second column
|
||||
cy.get(getElementFromAlias('column-1')).clear().type(getColName(1));
|
||||
tableColumnTypeSelector('col-type-1');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_text'))
|
||||
.first()
|
||||
.click();
|
||||
cy.get(getElementFromAlias('col-default-1')).clear();
|
||||
// Set primary key
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
|
||||
cy.get(getElementFromAlias('primary-key-select-1')).select('1');
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(10000);
|
||||
// Check if the table got created and navigatied to modify table
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
cy.get(getElementFromAlias(getTableName(0, testName)));
|
||||
// Validate
|
||||
validateCT(getTableName(0, testName), ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const passCTWithFK = () => {
|
||||
// go to create-table
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
// cy.get(getElementFromAlias('table-create')).click();
|
||||
// Set tablename
|
||||
cy.get(getElementFromAlias('tableName'))
|
||||
.clear()
|
||||
.type(getTableName(1, testName));
|
||||
// Set first column
|
||||
cy.get(getElementFromAlias('column-0')).type(getColName(0));
|
||||
tableColumnTypeSelector('col-type-0');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
|
||||
.first()
|
||||
.click();
|
||||
// Set second column
|
||||
cy.get(getElementFromAlias('column-1')).type(getColName(1));
|
||||
tableColumnTypeSelector('col-type-1');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_text'))
|
||||
.first()
|
||||
.click();
|
||||
// Set third column
|
||||
cy.get(getElementFromAlias('column-2')).type(getColName(2));
|
||||
tableColumnTypeSelector('col-type-2');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_text'))
|
||||
.first()
|
||||
.click();
|
||||
|
||||
// Set primary key
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
|
||||
// Set foreign key
|
||||
cy.get(getElementFromAlias('add-table-edit-fk-0')).click();
|
||||
cy.get(getElementFromAlias('foreign-key-ref-table-0')).select(
|
||||
getTableName(0, testName)
|
||||
);
|
||||
cy.get(getElementFromAlias('foreign-key-0-lcol-0')).select('0');
|
||||
cy.get(getElementFromAlias('foreign-key-0-rcol-0')).select(getColName(0));
|
||||
cy.get(getElementFromAlias('foreign-key-0-lcol-1')).select('1');
|
||||
cy.get(getElementFromAlias('foreign-key-0-rcol-1')).select(getColName(1));
|
||||
cy.get(getElementFromAlias('foreign-key-0-onUpdate-cascade')).check();
|
||||
cy.get(getElementFromAlias('foreign-key-0-onDelete-cascade')).check();
|
||||
|
||||
// set unique key 1
|
||||
cy.get(getElementFromAlias('add-table-edit-unique-key-0')).click();
|
||||
cy.get(getElementFromAlias('unique-key-0-column-0')).select('1');
|
||||
|
||||
// set unique key 2
|
||||
cy.get(getElementFromAlias('add-table-edit-unique-key-1')).click();
|
||||
cy.get(getElementFromAlias('unique-key-1-column-0')).select('1');
|
||||
cy.get(getElementFromAlias('unique-key-1-column-1')).select('2');
|
||||
cy.get(getElementFromAlias('unique-key-1-column-2')).select('0');
|
||||
cy.get(getElementFromAlias('remove-uk-1-column-1')).click();
|
||||
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(10000);
|
||||
// Check if the table got created and navigatied to modify table
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
1,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
cy.get('div').contains(
|
||||
`${getTableName(1, testName)}_${getColName(1)}_${getColName(0)}`
|
||||
);
|
||||
cy.get(getElementFromAlias(getTableName(1, testName)));
|
||||
// Validate
|
||||
validateCT(getTableName(1, testName), ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const failCTDuplicateTable = () => {
|
||||
// Visit data page
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
// Type table name
|
||||
cy.get(getElementFromAlias('tableName')).type(getTableName(0, testName));
|
||||
// Set column
|
||||
cy.get(getElementFromAlias('column-0')).type(getColName(1));
|
||||
tableColumnTypeSelector('col-type-0');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
|
||||
.first()
|
||||
.click();
|
||||
// Set primary key
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(7000);
|
||||
};
|
||||
|
||||
const deleteTable = (tableName: string) => {
|
||||
cy.get(getElementFromAlias(tableName)).click();
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
|
||||
setPromptValue(tableName);
|
||||
|
||||
// Click on delete
|
||||
cy.get(getElementFromAlias('delete-table')).click();
|
||||
// Confirm
|
||||
cy.window().its('prompt').should('be.called');
|
||||
|
||||
cy.wait(5000);
|
||||
validateCT(tableName, ResultType.FAILURE);
|
||||
};
|
||||
|
||||
export const deleteCTTestTables = () => {
|
||||
// Go to the modify section of the second table
|
||||
const secondTableName = getTableName(1, testName);
|
||||
deleteTable(secondTableName);
|
||||
// Go to the modify section of the first table
|
||||
const firstTableName = getTableName(0, testName);
|
||||
deleteTable(firstTableName);
|
||||
|
||||
// Match the URL
|
||||
|
||||
// FIXME: Temporarily disabling this.
|
||||
// cy.url().should('eq', `${baseUrl}/data/schema`);
|
||||
};
|
||||
|
||||
export const setValidationMetaData = () => {
|
||||
setMetaData();
|
||||
};
|
@ -1,48 +0,0 @@
|
||||
import { testMode } from '../../../helpers/common';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
|
||||
import {
|
||||
checkCreateTableRoute,
|
||||
failCTWithoutColumns,
|
||||
failCTWithoutPK,
|
||||
failCTDuplicateColumns,
|
||||
failCTWrongDefaultValue,
|
||||
passCT,
|
||||
failCTDuplicateTable,
|
||||
deleteCTTestTables,
|
||||
passCTWithFK,
|
||||
} from './spec';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
describe('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
// Visit the index route
|
||||
cy.visit(getIndexRoute());
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runCreateTableTests = () => {
|
||||
describe('Create Table', () => {
|
||||
it('Create table button opens the correct route', checkCreateTableRoute);
|
||||
it('Fails to create table without columns', failCTWithoutColumns);
|
||||
it('Fails to create table without primary key', failCTWithoutPK);
|
||||
it('Fails to create with duplicate columns', failCTDuplicateColumns);
|
||||
it('Fails to create with wrong default value', failCTWrongDefaultValue);
|
||||
it('Successfuly creates table', passCT);
|
||||
it(
|
||||
'Successfuly creates table with composite foreign and unique key',
|
||||
passCTWithFK
|
||||
);
|
||||
it('Fails to create duplicate table', failCTDuplicateTable);
|
||||
it('Delete the test tables', deleteCTTestTables);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runCreateTableTests();
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
import { baseUrl, getElementFromAlias } from '../../../helpers/dataHelpers';
|
||||
|
||||
const statements = {
|
||||
createTableSql:
|
||||
'CREATE TABLE a_test_test_article (id serial PRIMARY KEY, title text, content text);',
|
||||
createCustomFuncSql: `CREATE FUNCTION a_test_test_search_articles(search text)
|
||||
RETURNS SETOF a_test_test_article AS $function$
|
||||
SELECT *
|
||||
FROM a_test_test_article
|
||||
WHERE
|
||||
title ilike ('%' || search || '%')
|
||||
OR content ilike ('%' || search || '%')
|
||||
$function$ LANGUAGE sql STABLE;`,
|
||||
insertData_a1: `INSERT INTO a_test_test_article(title, content) VALUES ('hasura is awesome', 'I mean duh?!');`,
|
||||
insertData_a2: `INSERT INTO a_test_test_article(title, content) VALUES ('cloud lauched', 'hasura <3 the cloud');`,
|
||||
deleteFunction: 'DROP FUNCTION a_test_test_search_articles(search text);',
|
||||
cleanUpSql: 'DROP TABLE a_test_test_article CASCADE;',
|
||||
graphql: {
|
||||
query: `{
|
||||
a_test_test_search_articles
|
||||
(args: {{} search: "hasura" }) {
|
||||
id
|
||||
title
|
||||
content
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
export const openRawSQL = () => {
|
||||
cy.get('a').contains('Data').click();
|
||||
cy.wait(3000);
|
||||
cy.get(getElementFromAlias('sql-link')).click();
|
||||
cy.wait(3000);
|
||||
cy.url().should('eq', `${baseUrl}/data/sql`);
|
||||
};
|
||||
|
||||
const clearText = () => {
|
||||
cy.get('textarea').type('{selectall}', { force: true });
|
||||
cy.get('textarea').trigger('keydown', {
|
||||
keyCode: 46,
|
||||
which: 46,
|
||||
force: true,
|
||||
});
|
||||
cy.wait(2000);
|
||||
};
|
||||
|
||||
// helper to type into the SQL textarea on rawsql page
|
||||
const typeStatement = (
|
||||
statement: string,
|
||||
shouldClearText = false,
|
||||
waitTimeUponType = 2000,
|
||||
endWaitTime = 5000,
|
||||
unCheckTrackFunction = false
|
||||
) => {
|
||||
if (shouldClearText) {
|
||||
clearText();
|
||||
}
|
||||
cy.get('textarea').type(statement, { force: true });
|
||||
cy.wait(waitTimeUponType);
|
||||
if (unCheckTrackFunction) {
|
||||
cy.get(getElementFromAlias('raw-sql-track-check')).uncheck();
|
||||
}
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
// FIXME: maybe necessary for CLI mode
|
||||
// cy.get(getElementFromAlias('raw-sql-statement-timeout')).should('be.disabled');
|
||||
cy.wait(endWaitTime);
|
||||
};
|
||||
|
||||
export const createTableArticle = () =>
|
||||
typeStatement(statements.createTableSql);
|
||||
|
||||
export const createCustomFunction = () =>
|
||||
typeStatement(statements.createCustomFuncSql, true, 2000, 5000, true);
|
||||
|
||||
export const insertAuthorsIntoTable = () => {
|
||||
typeStatement(statements.insertData_a1, true);
|
||||
typeStatement(statements.insertData_a2, true);
|
||||
clearText();
|
||||
};
|
||||
|
||||
export const trackCustomFn = () => {
|
||||
cy.visit('/data/default/schema/public');
|
||||
cy.wait(7000);
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public`);
|
||||
|
||||
// Track Function
|
||||
cy.get(
|
||||
getElementFromAlias('add-track-function-a_test_test_search_articles')
|
||||
).click();
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const routeToGraphiql = () => {
|
||||
cy.visit('/api/api-explorer');
|
||||
cy.wait(7000);
|
||||
cy.url().should('eq', `${baseUrl}/api/api-explorer`);
|
||||
};
|
||||
|
||||
export const verifyCustomFnResult = () => {
|
||||
// Type the query
|
||||
cy.get('textarea')
|
||||
.first()
|
||||
.type(`{enter}{uparrow}${statements.graphql.query}`, { force: true });
|
||||
cy.wait(2000);
|
||||
cy.get('.execute-button').click();
|
||||
// verify if article is present
|
||||
|
||||
cy.get('.cm-property').contains('title');
|
||||
cy.get('.cm-property').contains('content');
|
||||
|
||||
cy.wait(2000);
|
||||
};
|
||||
|
||||
export const cleanUpSql = () => {
|
||||
typeStatement(statements.deleteFunction, true);
|
||||
typeStatement(statements.cleanUpSql, true);
|
||||
clearText();
|
||||
cy.wait(2000);
|
||||
};
|
||||
|
||||
export const routeToSQLPage = () => {
|
||||
cy.visit('/data/sql');
|
||||
cy.wait(7000);
|
||||
cy.url().should('eq', `${baseUrl}/data/sql`);
|
||||
};
|
@ -1,43 +0,0 @@
|
||||
import {
|
||||
openRawSQL,
|
||||
createTableArticle,
|
||||
createCustomFunction,
|
||||
insertAuthorsIntoTable,
|
||||
cleanUpSql,
|
||||
trackCustomFn,
|
||||
routeToGraphiql,
|
||||
verifyCustomFnResult,
|
||||
routeToSQLPage,
|
||||
} from './spec';
|
||||
import { testMode } from '../../../helpers/common';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
describe('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
cy.visit(getIndexRoute());
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runCustomFunctionTests = () => {
|
||||
describe('Custom Functions', () => {
|
||||
it('Open Raw SQL page', openRawSQL);
|
||||
it('Create test table', createTableArticle);
|
||||
it('Run SQL for custom function', createCustomFunction);
|
||||
it('Insert articles into table', insertAuthorsIntoTable);
|
||||
it('Track custom function', trackCustomFn);
|
||||
it('Route to GraphiQL page', routeToGraphiql);
|
||||
it('Check custom function results on GraphiQL', verifyCustomFnResult);
|
||||
it('Route to Raw SQL page', routeToSQLPage);
|
||||
it('Test cleanup', cleanUpSql);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runCustomFunctionTests();
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,198 +0,0 @@
|
||||
import {
|
||||
getElementFromAlias,
|
||||
baseUrl,
|
||||
getCustomFunctionName,
|
||||
getSchema,
|
||||
dropTable,
|
||||
testCustomFunctionSQLWithSessArg,
|
||||
getTrackFnPayload,
|
||||
createFunctionTable,
|
||||
trackCreateFunctionTable,
|
||||
getCreateTestFunctionQuery,
|
||||
getTrackTestFunctionQuery,
|
||||
createSampleTable,
|
||||
getTrackSampleTableQuery,
|
||||
createVolatileFunction,
|
||||
dropTableIfExists,
|
||||
} from '../../../helpers/dataHelpers';
|
||||
|
||||
import {
|
||||
dataRequest,
|
||||
validateCFunc,
|
||||
validateUntrackedFunc,
|
||||
ResultType,
|
||||
trackFunctionRequest,
|
||||
} from '../../validators/validators';
|
||||
import { setPromptValue } from '../../../helpers/common';
|
||||
|
||||
export const createCustomFunctionSuccess = () => {
|
||||
// Round about way to create a function
|
||||
dataRequest(createFunctionTable(), ResultType.SUCCESS, 'query');
|
||||
cy.wait(5000);
|
||||
dataRequest(trackCreateFunctionTable(), ResultType.SUCCESS, 'metadata');
|
||||
cy.wait(5000);
|
||||
|
||||
dataRequest(getCreateTestFunctionQuery(1), ResultType.SUCCESS, 'query');
|
||||
cy.wait(5000);
|
||||
dataRequest(getTrackTestFunctionQuery(1), ResultType.SUCCESS, 'metadata');
|
||||
cy.wait(5000);
|
||||
|
||||
// Check if the track checkbox is clicked or not
|
||||
validateCFunc(getCustomFunctionName(1), getSchema(), ResultType.SUCCESS);
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const unTrackFunction = () => {
|
||||
cy.visit(
|
||||
`data/default/schema/public/functions/${getCustomFunctionName(1)}/modify`
|
||||
);
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('custom-function-edit-untrack-btn')).click();
|
||||
cy.wait(5000);
|
||||
validateUntrackedFunc(
|
||||
getCustomFunctionName(1),
|
||||
getSchema(),
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const trackFunction = () => {
|
||||
cy.get(
|
||||
getElementFromAlias(`add-track-function-${getCustomFunctionName(1)}`)
|
||||
).should('exist');
|
||||
cy.get(
|
||||
getElementFromAlias(`add-track-function-${getCustomFunctionName(1)}`)
|
||||
).click();
|
||||
cy.get(getElementFromAlias(`track-as-mutation`)).should('exist');
|
||||
cy.get(getElementFromAlias(`track-as-mutation`)).click();
|
||||
cy.wait(5000);
|
||||
validateCFunc(getCustomFunctionName(1), getSchema(), ResultType.SUCCESS);
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const testSessVariable = () => {
|
||||
const fN = 'customFunctionWithSessionArg'.toLowerCase();
|
||||
dataRequest(
|
||||
dropTableIfExists({ name: 'text_result', schema: 'public' }),
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
cy.wait(3000);
|
||||
dataRequest(createSampleTable(), ResultType.SUCCESS, 'query');
|
||||
cy.wait(3000);
|
||||
dataRequest(getTrackSampleTableQuery(), ResultType.SUCCESS, 'metadata');
|
||||
cy.wait(3000);
|
||||
|
||||
dataRequest(testCustomFunctionSQLWithSessArg(fN), ResultType.SUCCESS);
|
||||
cy.wait(3000);
|
||||
|
||||
trackFunctionRequest(getTrackFnPayload(fN), ResultType.SUCCESS);
|
||||
cy.wait(5000);
|
||||
|
||||
cy.visit(`data/default/schema/public/functions/${fN}/modify`);
|
||||
cy.get(getElementFromAlias(`${fN}-session-argument-btn`), {
|
||||
timeout: 5000,
|
||||
}).click();
|
||||
|
||||
// invalid data should fail
|
||||
cy.get(getElementFromAlias(`${fN}-edit-sessvar-function-field`))
|
||||
.clear()
|
||||
.type('invalid');
|
||||
cy.get(getElementFromAlias(`${fN}-session-argument-save`)).click();
|
||||
|
||||
cy.expectErrorNotificationWithTitle(
|
||||
'Updating Session argument variable failed'
|
||||
);
|
||||
|
||||
cy.get(getElementFromAlias(`${fN}-session-argument-btn`), {
|
||||
timeout: 1000,
|
||||
}).click();
|
||||
cy.get(getElementFromAlias(`${fN}-edit-sessvar-function-field`))
|
||||
.clear()
|
||||
.type('hasura_session');
|
||||
cy.get(getElementFromAlias(`${fN}-session-argument-save`)).click();
|
||||
cy.wait(3000);
|
||||
cy.get(getElementFromAlias(fN)).should('be.visible');
|
||||
cy.visit(`data/default/schema/public/functions/${fN}/modify`);
|
||||
cy.wait(3000);
|
||||
cy.get(getElementFromAlias(`${fN}-session-argument`)).should(
|
||||
'contain',
|
||||
'hasura_session'
|
||||
);
|
||||
dataRequest(dropTable('text_result', true), ResultType.SUCCESS);
|
||||
cy.wait(3000);
|
||||
cy.visit(`data/default/schema/public/`);
|
||||
};
|
||||
|
||||
export const verifyPermissionTab = () => {
|
||||
cy.get(getElementFromAlias('functions-data/default-permissions')).click();
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('custom-function-permission-link')).should(
|
||||
'exist'
|
||||
);
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const deleteCustomFunction = () => {
|
||||
cy.get(getElementFromAlias('functions-data/default-modify')).click();
|
||||
|
||||
setPromptValue(getCustomFunctionName(1));
|
||||
|
||||
cy.get(getElementFromAlias('custom-function-edit-delete-btn')).click();
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('delete-confirmation-error')).should('not.exist');
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public`);
|
||||
cy.wait(5000);
|
||||
|
||||
dataRequest(dropTable(), ResultType.SUCCESS);
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const trackVolatileFunction = () => {
|
||||
const fN = 'customVolatileFunc'.toLowerCase();
|
||||
dataRequest(
|
||||
dropTableIfExists({ name: 'text_result', schema: 'public' }),
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
cy.wait(5000);
|
||||
dataRequest(createSampleTable(), ResultType.SUCCESS);
|
||||
cy.wait(5000);
|
||||
dataRequest(getTrackSampleTableQuery(), ResultType.SUCCESS, 'metadata');
|
||||
dataRequest(createVolatileFunction(fN), ResultType.SUCCESS);
|
||||
cy.wait(5000);
|
||||
cy.visit(`data/default/schema/public`);
|
||||
cy.get(getElementFromAlias(`add-track-function-${fN}`)).click();
|
||||
cy.get(getElementFromAlias('track-as-mutation')).click();
|
||||
cy.wait(2000);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/functions/${fN}/modify`
|
||||
);
|
||||
dataRequest(dropTable('text_result', true), ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const trackVolatileFunctionAsQuery = () => {
|
||||
const fN = 'customVolatileFunc'.toLowerCase();
|
||||
dataRequest(
|
||||
dropTableIfExists({ name: 'text_result', schema: 'public' }),
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
cy.wait(5000);
|
||||
dataRequest(createSampleTable(), ResultType.SUCCESS);
|
||||
cy.wait(5000);
|
||||
dataRequest(getTrackSampleTableQuery(), ResultType.SUCCESS, 'metadata');
|
||||
dataRequest(createVolatileFunction(fN), ResultType.SUCCESS);
|
||||
cy.wait(5000);
|
||||
cy.visit(`data/default/schema/public`);
|
||||
cy.get(getElementFromAlias(`add-track-function-${fN}`)).click();
|
||||
cy.get(getElementFromAlias('track-as-query')).click();
|
||||
cy.wait(2000);
|
||||
cy.get(getElementFromAlias('track-as-query-confirm')).click();
|
||||
cy.wait(2000);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/functions/${fN}/modify`
|
||||
);
|
||||
dataRequest(dropTable('text_result', true), ResultType.SUCCESS);
|
||||
};
|
@ -1,43 +0,0 @@
|
||||
import { testMode } from '../../../helpers/common';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
|
||||
import {
|
||||
createCustomFunctionSuccess,
|
||||
deleteCustomFunction,
|
||||
unTrackFunction,
|
||||
trackFunction,
|
||||
verifyPermissionTab,
|
||||
trackVolatileFunction,
|
||||
trackVolatileFunctionAsQuery,
|
||||
} from './spec';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
describe('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
// Visit the index route
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(5000);
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runCreateCustomFunctionsTableTests = () => {
|
||||
describe('Custom Function Tests', () => {
|
||||
it('Create a custom function and track', createCustomFunctionSuccess);
|
||||
it('Untrack custom function', unTrackFunction);
|
||||
it('Track custom function', trackFunction);
|
||||
it('Verify permission tab', verifyPermissionTab);
|
||||
it('Delete custom function', deleteCustomFunction);
|
||||
// TODO it('Test custom function with Session Argument', testSessVariable);
|
||||
it('Tracks VOLATILE function as mutation', trackVolatileFunction);
|
||||
it('Tracks VOLATILE function as query', trackVolatileFunctionAsQuery);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runCreateCustomFunctionsTableTests();
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,533 +0,0 @@
|
||||
import {
|
||||
baseUrl,
|
||||
getColName,
|
||||
getTableName,
|
||||
dataTypes,
|
||||
getElementFromAlias,
|
||||
typeDefaults,
|
||||
tableColumnTypeSelector,
|
||||
getIndexRoute,
|
||||
} from '../../../helpers/dataHelpers';
|
||||
|
||||
import {
|
||||
validateInsert,
|
||||
setMetaData,
|
||||
validateCT,
|
||||
ResultType,
|
||||
} from '../../validators/validators';
|
||||
import { setPromptValue } from '../../../helpers/common';
|
||||
|
||||
const numOfDataTypes = dataTypes.length;
|
||||
const testName = 'ib';
|
||||
|
||||
//* ******************** Util functions ************************
|
||||
|
||||
const setColumns = () => {
|
||||
for (let i = 0; i < numOfDataTypes; i += 1) {
|
||||
// Type column name
|
||||
cy.get(getElementFromAlias(`column-${i}`)).type(getColName(i));
|
||||
// Select column type
|
||||
tableColumnTypeSelector(`col-type-${i}`);
|
||||
cy.get(getElementFromAlias(`data_test_column_type_value_${dataTypes[i]}`))
|
||||
.first()
|
||||
.click();
|
||||
|
||||
if (i === dataTypes.indexOf('text')) {
|
||||
cy.get(getElementFromAlias(`unique-${i}`)).check({ force: true });
|
||||
}
|
||||
// Set appropriate default if the type is not serial
|
||||
if (i > 1) {
|
||||
cy.get(getElementFromAlias(`col-default-${i}`)).type(
|
||||
typeDefaults[dataTypes[i]]
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const clickSaveOrInsert = (firstIndex: number, currentIndex: number) => {
|
||||
if (currentIndex === firstIndex) {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
} else {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
}
|
||||
cy.wait(2000);
|
||||
};
|
||||
|
||||
const checkQuerySuccess = () => {
|
||||
// Expect only 4 rows i.e. expect fifth element to not exist
|
||||
cy.get('[role=gridcell]').contains('filter-text');
|
||||
cy.get('[role=row]').eq(2).should('not.exist');
|
||||
};
|
||||
|
||||
const checkOrder = (order: string) => {
|
||||
// Utility function to get right element
|
||||
|
||||
if (order === 'asc') {
|
||||
cy.get('[role=row]').each(($el, index) => {
|
||||
if (index !== 0) {
|
||||
cy.wrap($el)
|
||||
.find('[role=gridcell]')
|
||||
.first()
|
||||
.next()
|
||||
.next()
|
||||
.contains(index);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cy.get('[role=row]').each(($el, index) => {
|
||||
if (index !== 0) {
|
||||
cy.wrap($el)
|
||||
.find('[role=gridcell]')
|
||||
.first()
|
||||
.next()
|
||||
.next()
|
||||
.contains(22 - index);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//* ******************** Test functions ***************************
|
||||
|
||||
export const passBICreateTable = () => {
|
||||
cy.wait(7000);
|
||||
// Click create table button
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
// Type table name
|
||||
cy.get(getElementFromAlias('tableName')).type(getTableName(0, testName));
|
||||
// Set columns with all fields
|
||||
setColumns();
|
||||
// Set primary key
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(7000);
|
||||
validateCT(getTableName(0, testName), ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const passSearchTables = () => {
|
||||
// Click add table button
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
// Type table name
|
||||
cy.get(getElementFromAlias('tableName')).type(getTableName(1, testName));
|
||||
// Type column name
|
||||
cy.get(getElementFromAlias('column-0')).type(getColName(0));
|
||||
tableColumnTypeSelector('col-type-0');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
|
||||
.first()
|
||||
.click();
|
||||
// Set primary key
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(7000);
|
||||
validateCT(getTableName(0, testName), ResultType.SUCCESS);
|
||||
// cy.get(getElementFromAlias('search-tables')).type('0');
|
||||
// cy.get(getElementFromAlias('table-links')).should('not.contain', '1');
|
||||
// cy.get(getElementFromAlias('search-tables')).type('{home}{del}');
|
||||
};
|
||||
|
||||
export const checkInsertRoute = () => {
|
||||
// Click on Insert tab
|
||||
cy.get(getElementFromAlias(getTableName(0, testName))).click();
|
||||
cy.get(getElementFromAlias('table-insert-rows')).click();
|
||||
// Match URL
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/insert`
|
||||
);
|
||||
};
|
||||
|
||||
export const failBIWrongDataType = () => {
|
||||
// Check if the table creation fails for wrong inputs of each data type
|
||||
for (let i = 2; i < numOfDataTypes; i += 1) {
|
||||
// Text and Boolean always succeed, so we check only for others
|
||||
if (dataTypes[i] !== 'text' && dataTypes[i] !== 'boolean') {
|
||||
const sureFailString = 'abcd1234';
|
||||
// Type a string that fails
|
||||
cy.get(getElementFromAlias(`typed-input-${i}`)).type(sureFailString);
|
||||
// Click the Save/Insert Again button.
|
||||
clickSaveOrInsert(2, i);
|
||||
cy.get(getElementFromAlias(`typed-input-${i}`)).clear();
|
||||
// Check the default radio of current column
|
||||
cy.get(getElementFromAlias(`typed-input-default-${i}`)).check();
|
||||
}
|
||||
|
||||
validateInsert(getTableName(0, testName), 0);
|
||||
}
|
||||
};
|
||||
|
||||
export const passBIInsert20Rows = () => {
|
||||
for (let i = 0; i < 20; i += 1) {
|
||||
// Type a string in the text type fields of some rows (to be tested in Browse rows)
|
||||
const textIndex = dataTypes.indexOf('text');
|
||||
// Click the Insert Again button.
|
||||
if (i === 0) {
|
||||
cy.get(getElementFromAlias(`typed-input-${textIndex}`)).type(
|
||||
'{selectall}{del}'
|
||||
);
|
||||
cy.get(getElementFromAlias(`typed-input-${textIndex}`)).type(
|
||||
'filter-text'
|
||||
);
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
} else {
|
||||
cy.get(getElementFromAlias(`typed-input-${textIndex}`)).type(
|
||||
'{selectall}{del}'
|
||||
);
|
||||
cy.get(getElementFromAlias(`typed-input-${textIndex}`))
|
||||
.type('{selectall}{del}')
|
||||
.type(Math.random().toString(36).substring(7));
|
||||
cy.get(
|
||||
getElementFromAlias(`typed-input-default-${textIndex + 1}`)
|
||||
).check();
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
cy.wait(300);
|
||||
validateInsert(getTableName(0, testName), i + 1);
|
||||
}
|
||||
}
|
||||
// Wait for insert notifications to disappear
|
||||
cy.wait(7000);
|
||||
};
|
||||
|
||||
export const checkBrowseRoute = () => {
|
||||
// Click on Browse tab
|
||||
cy.get(getElementFromAlias(getTableName(0, testName))).click();
|
||||
cy.get(getElementFromAlias('table-browse-rows')).click();
|
||||
cy.wait(2000);
|
||||
// Match URL
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/browse`
|
||||
);
|
||||
};
|
||||
|
||||
export const passBI20RowsExist = () => {
|
||||
// Check if the 20 inserted elements reflect in the UI
|
||||
cy.get(getElementFromAlias('pagination-select')).select('20');
|
||||
// cy.get(getElementFromAlias('table-browse-rows')).contains('20');
|
||||
};
|
||||
|
||||
export const checkPagination = () => {
|
||||
// Check if the current page is 1
|
||||
cy.get('.-pageJump > input').should('have.value', '1');
|
||||
// Check if the total number of pages is 3
|
||||
cy.get('.-totalPages').contains('3');
|
||||
// Check if the default value of rows displayed is 10
|
||||
cy.get('.-pageSizeOptions > select').should('have.value', '10');
|
||||
cy.get('.-next > button').click({ force: true });
|
||||
cy.wait(3000);
|
||||
// Check if the page changed
|
||||
cy.get(
|
||||
'.rt-tbody > div:nth-child(1) > div > div:nth-child(3) > div'
|
||||
).contains('11');
|
||||
cy.get('.-pageJump > input').should('have.value', '2');
|
||||
cy.get('.-previous > button').click({ force: true });
|
||||
cy.wait(3000);
|
||||
// Check if the page changed
|
||||
cy.get('.-pageJump > input').should('have.value', '1');
|
||||
cy.get('.-pageSizeOptions > select').select('5 rows');
|
||||
cy.wait(3000);
|
||||
// Check if the total number of pages changed
|
||||
cy.get('.-totalPages').contains('5');
|
||||
};
|
||||
|
||||
export const passBISort = (order: string) => {
|
||||
cy.wait(7000);
|
||||
// Select column with type 'serial'
|
||||
const serialIndex = dataTypes.indexOf('serial');
|
||||
cy.get(getElementFromAlias('sort-column-0')).select(getColName(serialIndex), {
|
||||
force: true,
|
||||
});
|
||||
// Select order as `descending`
|
||||
cy.get(getElementFromAlias('sort-order-0')).select(
|
||||
order === 'asc' ? 'Asc' : 'Desc',
|
||||
{ force: true }
|
||||
);
|
||||
// Run query
|
||||
cy.get(getElementFromAlias('run-query')).click();
|
||||
cy.wait(5000);
|
||||
// Check order
|
||||
checkOrder(order);
|
||||
|
||||
// Clear filter
|
||||
cy.get(getElementFromAlias('clear-sorts-0')).click({ force: true });
|
||||
// Run query
|
||||
cy.get(getElementFromAlias('run-query')).click();
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const passBIFilterQueryEq = () => {
|
||||
// Select column with type "text"
|
||||
const textIndex = dataTypes.indexOf('text');
|
||||
cy.get(getElementFromAlias('filter-column-0')).select(getColName(textIndex));
|
||||
// Select operator as `eq`
|
||||
cy.get(getElementFromAlias('filter-op-0')).select('$eq');
|
||||
// Type value as "filter-text"
|
||||
cy.get("input[placeholder='-- value --']").last().type('filter-text');
|
||||
// Run query
|
||||
cy.get(getElementFromAlias('run-query')).click();
|
||||
cy.wait(2000);
|
||||
// Check if the query was successful
|
||||
checkQuerySuccess();
|
||||
|
||||
// Clear filter
|
||||
cy.get(getElementFromAlias('clear-filter-0')).click();
|
||||
// Run query
|
||||
cy.get(getElementFromAlias('run-query')).click();
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const deleteBITestTable = () => {
|
||||
cy.get(getElementFromAlias(getTableName(2, testName))).click();
|
||||
// Go to the modify section of the table
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
cy.wait(2000);
|
||||
setPromptValue(getTableName(2, testName));
|
||||
// Click on delete
|
||||
cy.get(getElementFromAlias('delete-table')).click();
|
||||
// Confirm
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(7000);
|
||||
// Match the URL
|
||||
// FIXME: change this later
|
||||
// cy.url().should('eq', `${baseUrl}/data/default/schema/public`);
|
||||
validateCT(getTableName(2, testName), ResultType.FAILURE);
|
||||
|
||||
cy.get(getElementFromAlias(getTableName(1, testName))).click();
|
||||
// Go to the modify section of the table
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
cy.wait(2000);
|
||||
setPromptValue(getTableName(1, testName));
|
||||
// Click on delete
|
||||
cy.get(getElementFromAlias('delete-table')).click();
|
||||
// Confirm
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(7000);
|
||||
// Match the URL
|
||||
// FIXME: change this later
|
||||
// cy.url().should('eq', `${baseUrl}/data/schema`);
|
||||
validateCT(getTableName(1, testName), ResultType.FAILURE);
|
||||
|
||||
cy.get(getElementFromAlias(getTableName(0, testName))).click();
|
||||
// Go to the modify section of the table
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
setPromptValue(getTableName(0, testName));
|
||||
cy.wait(2000);
|
||||
// Click on delete
|
||||
cy.get(getElementFromAlias('delete-table')).click();
|
||||
// Confirm
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(7000);
|
||||
|
||||
// Match the URL
|
||||
// FIXME: change later
|
||||
// cy.url().should('eq', `${baseUrl}/data/schema`);
|
||||
validateCT(getTableName(0, testName), ResultType.FAILURE);
|
||||
};
|
||||
|
||||
export const failBIUniqueKeys = () => {
|
||||
// Type a string in the text type fields of some rows (to be tested in Browse rows)
|
||||
const textIndex = dataTypes.indexOf('text');
|
||||
const floatIndex = dataTypes.indexOf('numeric');
|
||||
cy.get(getElementFromAlias(`typed-input-${floatIndex}`)).type(`${0.5555}`);
|
||||
cy.get(getElementFromAlias(`typed-input-${textIndex}`))
|
||||
.clear()
|
||||
.type('filter-text');
|
||||
|
||||
// Click the Insert Again button.
|
||||
cy.get(getElementFromAlias(`typed-input-${textIndex}`)).type(
|
||||
'{selectall}{del}'
|
||||
);
|
||||
cy.get(getElementFromAlias(`typed-input-${textIndex}`)).type('name');
|
||||
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
// Check default for next insert
|
||||
|
||||
cy.get(getElementFromAlias(`typed-input-default-${textIndex}`)).check();
|
||||
|
||||
validateInsert(getTableName(0, testName), 21);
|
||||
cy.wait(7000);
|
||||
cy.get(getElementFromAlias(`typed-input-${textIndex}`))
|
||||
.clear()
|
||||
.type('filter-text');
|
||||
// Click the Insert Again button.
|
||||
cy.get(getElementFromAlias(`typed-input-${textIndex}`)).type(
|
||||
'{selectall}{del}'
|
||||
);
|
||||
cy.get(getElementFromAlias(`typed-input-${textIndex}`)).type('name');
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
|
||||
cy.wait(7000);
|
||||
validateInsert(getTableName(0, testName), 21);
|
||||
};
|
||||
export const setValidationMetaData = () => {
|
||||
setMetaData();
|
||||
};
|
||||
|
||||
export const passEditButton = () => {
|
||||
cy.get(getElementFromAlias('table-browse-rows')).click();
|
||||
cy.wait(2000);
|
||||
cy.get(getElementFromAlias('row-edit-button-0')).click();
|
||||
cy.wait(2000);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/edit`
|
||||
);
|
||||
const textIndex = dataTypes.indexOf('text');
|
||||
cy.get(getElementFromAlias(`typed-input-${textIndex}`)).type(
|
||||
'{selectall}{del}'
|
||||
);
|
||||
cy.get(getElementFromAlias(`typed-input-${textIndex}`)).type('new-text');
|
||||
cy.get(getElementFromAlias('edit-save-button')).click();
|
||||
cy.wait(7000);
|
||||
};
|
||||
|
||||
export const passCloneButton = () => {
|
||||
cy.get(getElementFromAlias('table-browse-rows')).click();
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('row-clone-button-0')).click();
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/insert`
|
||||
);
|
||||
cy.get(getElementFromAlias('clear-button')).click();
|
||||
cy.get(getElementFromAlias('typed-input-0')).should('have.value', '');
|
||||
};
|
||||
|
||||
export const checkViewRelationship = () => {
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
// Type table name
|
||||
cy.get(getElementFromAlias('tableName')).type(getTableName(2, testName));
|
||||
cy.get(getElementFromAlias('column-0')).type('id');
|
||||
tableColumnTypeSelector('col-type-0');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
|
||||
.first()
|
||||
.click();
|
||||
cy.get(getElementFromAlias('column-1')).type('someID');
|
||||
tableColumnTypeSelector('col-type-1');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
|
||||
.first()
|
||||
.click();
|
||||
// Set primary key
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
|
||||
// Click on create
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(7000);
|
||||
validateCT(getTableName(0, testName), ResultType.SUCCESS);
|
||||
// Add foreign key
|
||||
cy.get(getElementFromAlias('modify-table-edit-fk-0')).click();
|
||||
cy.get(getElementFromAlias('foreign-key-ref-table-0')).select(
|
||||
getTableName(0, testName)
|
||||
);
|
||||
cy.get(getElementFromAlias('foreign-key-0-lcol-0')).select('0');
|
||||
cy.get(getElementFromAlias('foreign-key-0-rcol-0')).select(getColName(0));
|
||||
cy.get(getElementFromAlias('modify-table-fk-0-save')).click();
|
||||
cy.wait(5000);
|
||||
// Add relationship
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.get(getElementFromAlias('obj-rel-add-0')).click();
|
||||
cy.get(getElementFromAlias('suggested-rel-name')).clear().type('someRel');
|
||||
cy.get(getElementFromAlias('obj-rel-save-0')).click();
|
||||
cy.wait(2000);
|
||||
// Insert a row
|
||||
cy.get(getElementFromAlias('table-insert-rows')).click();
|
||||
cy.get(getElementFromAlias('typed-input-1')).type('1');
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
cy.wait(1000);
|
||||
cy.get(getElementFromAlias('table-browse-rows')).click();
|
||||
cy.wait(1000);
|
||||
cy.get('.rt-table').within(() => {
|
||||
cy.get('a').contains('View').click();
|
||||
cy.wait(1000);
|
||||
});
|
||||
cy.get('a').contains('Close').first().click();
|
||||
};
|
||||
|
||||
export const passDeleteRow = () => {
|
||||
cy.get(getElementFromAlias('table-browse-rows')).click();
|
||||
cy.wait(5000);
|
||||
// cy.get(getElementFromAlias('table-browse-rows')).contains('21');
|
||||
cy.get(getElementFromAlias('row-delete-button-0')).click();
|
||||
cy.on('window:confirm', str => {
|
||||
expect(
|
||||
str.indexOf('This will permanently delete this row from this table') !==
|
||||
-1
|
||||
).to.be.true;
|
||||
});
|
||||
// cy.get(getElementFromAlias('table-browse-rows')).contains('20');
|
||||
cy.wait(14000);
|
||||
};
|
||||
|
||||
export const passBulkDeleteRows = () => {
|
||||
cy.get(getElementFromAlias('table-browse-rows')).click();
|
||||
cy.wait(5000);
|
||||
// cy.get(getElementFromAlias('table-browse-rows')).contains('20');
|
||||
cy.get(getElementFromAlias('row-checkbox-0')).click();
|
||||
cy.get(getElementFromAlias('row-checkbox-1')).click();
|
||||
cy.get(getElementFromAlias('bulk-delete')).click();
|
||||
cy.wait(1000);
|
||||
cy.on('window:confirm', str => {
|
||||
expect(
|
||||
str.indexOf('This will permanently delete rows from this table') !== -1
|
||||
).to.be.true;
|
||||
});
|
||||
// cy.get(getElementFromAlias('table-browse-rows')).contains('18');
|
||||
cy.wait(14000);
|
||||
};
|
||||
|
||||
export const passBulkDeleteAllRows = () => {
|
||||
cy.get(getElementFromAlias('table-browse-rows')).click();
|
||||
cy.wait(5000);
|
||||
// cy.get(getElementFromAlias('table-browse-rows')).contains('18');
|
||||
cy.get(getElementFromAlias('select-all-rows')).click();
|
||||
cy.get(getElementFromAlias('bulk-delete')).click();
|
||||
cy.wait(1000);
|
||||
cy.on('window:confirm', str => {
|
||||
expect(
|
||||
str.indexOf('This will permanently delete rows from this table') !== -1
|
||||
).to.be.true;
|
||||
});
|
||||
// cy.get(getElementFromAlias('table-browse-rows')).contains('(8)');
|
||||
cy.wait(14000);
|
||||
};
|
||||
|
||||
export const passArrayDataType = () => {
|
||||
// create new column
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
cy.wait(1000);
|
||||
cy.get(getElementFromAlias('modify-table-edit-add-new-column')).click();
|
||||
cy.get(getElementFromAlias('column-name')).type('array_column');
|
||||
cy.get(getElementFromAlias('col-type-0'))
|
||||
.children('div')
|
||||
.click()
|
||||
.find('input')
|
||||
.type('text[]', { force: true });
|
||||
cy.get(getElementFromAlias('modify-table-add-new-column-save')).click();
|
||||
|
||||
// insert new row
|
||||
cy.get(getElementFromAlias('table-insert-rows')).click();
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('typed-input-11')).type('["a", "b"]');
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
|
||||
// go to browse rows and check if row was added
|
||||
cy.get(getElementFromAlias('table-browse-rows')).click();
|
||||
cy.wait(5000);
|
||||
// cy.get(getElementFromAlias('table-browse-rows')).contains('(9)');
|
||||
};
|
@ -1,68 +0,0 @@
|
||||
import { testMode } from '../../../helpers/common';
|
||||
|
||||
import {
|
||||
passBICreateTable,
|
||||
deleteBITestTable,
|
||||
checkInsertRoute,
|
||||
failBIWrongDataType,
|
||||
failBIUniqueKeys,
|
||||
passBIInsert20Rows,
|
||||
checkBrowseRoute,
|
||||
passBISort,
|
||||
passBIFilterQueryEq,
|
||||
passEditButton,
|
||||
passSearchTables,
|
||||
passCloneButton,
|
||||
checkViewRelationship,
|
||||
passDeleteRow,
|
||||
passBulkDeleteRows,
|
||||
passBulkDeleteAllRows,
|
||||
passArrayDataType,
|
||||
} from './spec';
|
||||
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
cy.visit(getIndexRoute());
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runInsertBrowseTests = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Table: Browse and Insert', () => {
|
||||
it('Create a table with fields of all data types', passBICreateTable);
|
||||
it('Search for tables', passSearchTables);
|
||||
it('Check Insert Route', checkInsertRoute);
|
||||
it('Fails when entered wrong data type', failBIWrongDataType);
|
||||
it('Insert 20 rows', passBIInsert20Rows);
|
||||
it('Fail for adding same data for Unique keys', failBIUniqueKeys);
|
||||
it('Check browser rows route', checkBrowseRoute);
|
||||
// it('20 Inserted rows reflect in browse rows', passBI20RowsExist);
|
||||
// it('Check pagination in Browse Rows table', checkPagination);
|
||||
it('Ascending sort works as expected', () => passBISort('asc'));
|
||||
it('Descending sort works as expected', () => passBISort('desc'));
|
||||
it('Filter query works as expected with $eq', passBIFilterQueryEq);
|
||||
it('Check edit button', passEditButton);
|
||||
it('Check for clone clear', passCloneButton);
|
||||
it('Delete the row', passDeleteRow);
|
||||
it('Bulk delete rows', passBulkDeleteRows);
|
||||
it('Bulk delete all rows', passBulkDeleteAllRows);
|
||||
it('Handle array data types', passArrayDataType);
|
||||
it('Check view relationship', checkViewRelationship);
|
||||
it('Delete test table', deleteBITestTable);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runInsertBrowseTests();
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
import { setPromptWithCb } from '../../../helpers/common';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
|
||||
export const navigateAndOpenConnectDatabasesForm = () => {
|
||||
cy.location('pathname').then(currentPage => {
|
||||
const alreadyOnThePage = currentPage.startsWith('/data/manage');
|
||||
if (!alreadyOnThePage) {
|
||||
cy.log('**--- Navigate to Connect Databases Form**');
|
||||
|
||||
cy.log('**--- visit index route and set metadata**');
|
||||
cy.visit('/data/default/schema/public').then(setMetaData);
|
||||
|
||||
cy.log('**--- Click on the manage database menu**');
|
||||
cy.findByRole('button', { name: 'Manage' }).click();
|
||||
cy.location('pathname').should('eq', '/data/manage');
|
||||
}
|
||||
});
|
||||
|
||||
cy.log('**--- Click on the Connect Database button**');
|
||||
cy.findByRole('button', { name: 'Connect Database' }).click();
|
||||
cy.location('pathname').should('eq', '/data/manage/connect');
|
||||
|
||||
cy.get('form').within(() => {
|
||||
cy.log('**--- Click on Connection Settings section**');
|
||||
cy.contains('Connection Settings').click();
|
||||
});
|
||||
cy.get('form').within(() => {
|
||||
cy.log('**--- Click on GraphQL Field Customization section**');
|
||||
cy.contains('GraphQL Field Customization').click();
|
||||
});
|
||||
};
|
||||
|
||||
export const navigateToManageDatabases = () => {
|
||||
cy.log('**--- Visit index route and set metadata**');
|
||||
cy.visit('/data/default/schema/public').then(setMetaData);
|
||||
|
||||
cy.log('**--- Visit the manage database route**');
|
||||
cy.getBySel('sidebar-manage-database').click();
|
||||
cy.location('pathname').should('eq', '/data/manage');
|
||||
};
|
||||
|
||||
export const submitConnectDBForm = () => {
|
||||
cy.log('**--- Click on the Connect Database button**');
|
||||
cy.getBySel('connect-database-btn').click();
|
||||
};
|
||||
|
||||
export const removeDBFromList = (dbName: string) => {
|
||||
setPromptWithCb(dbName, () => {
|
||||
cy.log('**--- Click on the Remove Database button**');
|
||||
cy.getBySel(dbName).find('button').contains('Remove').click();
|
||||
});
|
||||
};
|
@ -1,160 +0,0 @@
|
||||
import { baseUrl, testMode } from '../../../helpers/common';
|
||||
import {
|
||||
navigateAndOpenConnectDatabasesForm,
|
||||
navigateToManageDatabases,
|
||||
removeDBFromList,
|
||||
submitConnectDBForm,
|
||||
} from './common.spec';
|
||||
import {
|
||||
createDB,
|
||||
fillDetailsForPgConnParamsForm,
|
||||
fillDetailsForPgDbUrlForm,
|
||||
fillDetailsForPgEnvVarForm,
|
||||
removeDB,
|
||||
} from './postgres.spec';
|
||||
|
||||
const connectPgDatabaseFormTests = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Add a database via connect form', () => {
|
||||
describe('can successfully add', () => {
|
||||
describe('a postgres database', () => {
|
||||
it('using a connection string', () => {
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Add postgres DB via connection string**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
navigateAndOpenConnectDatabasesForm();
|
||||
fillDetailsForPgDbUrlForm('postgres_db_with_url');
|
||||
submitConnectDBForm();
|
||||
|
||||
cy.log('**--- Notifies that DB is being added');
|
||||
cy.expectSuccessNotificationWithTitle('Adding data source...');
|
||||
|
||||
cy.log('**--- Redirects to Data Manager page');
|
||||
cy.getBySel('manage-database-section').within(() => {
|
||||
cy.findByText('Data Manager');
|
||||
});
|
||||
cy.url().should('eq', `${baseUrl}/data/manage`);
|
||||
|
||||
cy.log('**--- has postgres_db_with_url on manage page');
|
||||
cy.findByText('postgres_db_with_url');
|
||||
|
||||
cy.log('**--- has success notification displayed');
|
||||
cy.expectSuccessNotificationWithTitle(
|
||||
'Data source added successfully!'
|
||||
);
|
||||
|
||||
cy.log('**--- Remove database**');
|
||||
removeDB('postgres_db_with_url');
|
||||
});
|
||||
|
||||
it('using connection parameters', () => {
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Add postgres DB via connection parameters**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
navigateAndOpenConnectDatabasesForm();
|
||||
fillDetailsForPgConnParamsForm('postgres_db_with_conn_param');
|
||||
submitConnectDBForm();
|
||||
|
||||
cy.log('**--- notifies that db is being added');
|
||||
cy.expectSuccessNotificationWithTitle('Adding data source...');
|
||||
|
||||
cy.log('**--- redirects to Data Manager page');
|
||||
cy.getBySel('manage-database-section').within(() => {
|
||||
cy.findByText('Data Manager');
|
||||
});
|
||||
cy.url().should('eq', `${baseUrl}/data/manage`);
|
||||
|
||||
cy.log('**--- has postgres_db_with_conn_param on manage page');
|
||||
cy.findByText('postgres_db_with_conn_param');
|
||||
|
||||
cy.log('**--- has success notification displayed');
|
||||
cy.expectSuccessNotificationWithTitle(
|
||||
'Data source added successfully!'
|
||||
);
|
||||
|
||||
cy.log('**--- Remove database**');
|
||||
removeDB('postgres_db_with_conn_param');
|
||||
});
|
||||
|
||||
it('using environment variables', () => {
|
||||
cy.log('**------------------------------**');
|
||||
cy.log('**--- Add postgres DB via env vars**');
|
||||
cy.log('**------------------------------**');
|
||||
|
||||
navigateAndOpenConnectDatabasesForm();
|
||||
fillDetailsForPgEnvVarForm('postgres_db_with_env_var');
|
||||
submitConnectDBForm();
|
||||
|
||||
cy.log('**--- notifies that db is being added');
|
||||
cy.expectSuccessNotificationWithTitle('Adding data source...');
|
||||
|
||||
cy.log('**--- redirects to Data Manager page');
|
||||
cy.getBySel('manage-database-section').within(() => {
|
||||
cy.findByText('Data Manager');
|
||||
});
|
||||
cy.url().should('eq', `${baseUrl}/data/manage`);
|
||||
|
||||
cy.log('**--- has postgres_db_with_env_var on manage page');
|
||||
cy.findByText('postgres_db_with_env_var');
|
||||
|
||||
cy.log('**--- has success notification displayed');
|
||||
cy.expectSuccessNotificationWithTitle(
|
||||
'Data source added successfully!'
|
||||
);
|
||||
|
||||
cy.log('**--- Remove database**');
|
||||
removeDB('postgres_db_with_env_var');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fails on submitting', () => {
|
||||
describe('an empty form', () => {
|
||||
it('submit with no inputs filled in', () => {
|
||||
navigateAndOpenConnectDatabasesForm();
|
||||
cy.getBySel('connect-database-btn').click();
|
||||
cy.expectErrorNotification();
|
||||
});
|
||||
});
|
||||
|
||||
it('with a duplicate database name', () => {
|
||||
navigateAndOpenConnectDatabasesForm();
|
||||
createDB('test');
|
||||
fillDetailsForPgDbUrlForm('test');
|
||||
submitConnectDBForm();
|
||||
|
||||
cy.log('**--- verify error notification');
|
||||
cy.expectErrorNotificationWithTitle('Adding data source failed');
|
||||
|
||||
cy.log('**--- Remove database**');
|
||||
removeDB('test');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const manageDatabasesPageTests = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Connected Databases list page', () => {
|
||||
it('can successfully remove db', () => {
|
||||
cy.log('**--- Create database**');
|
||||
createDB('db_for_removal');
|
||||
navigateToManageDatabases();
|
||||
|
||||
cy.log('**--- use the remove button');
|
||||
removeDBFromList('db_for_removal');
|
||||
|
||||
cy.log('**--- verify success notification');
|
||||
cy.expectSuccessNotificationWithTitle('Data source removed successfully');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
connectPgDatabaseFormTests();
|
||||
manageDatabasesPageTests();
|
||||
}
|
@ -1,228 +0,0 @@
|
||||
const config = {
|
||||
host: 'postgres',
|
||||
port: '5432',
|
||||
dbName: 'postgres',
|
||||
username: 'postgres',
|
||||
password: 'postgrespassword',
|
||||
};
|
||||
|
||||
const dbUrl = `postgres://${config.username}:${config.password}@${config.host}:${config.port}/${config.dbName}`;
|
||||
|
||||
const graphqlCustomizationTest = () => {
|
||||
cy.log('**--- Select a graphql naming convention**');
|
||||
cy.get('[data-test="GraphQL Field Customization"]').within(() => {
|
||||
cy.get('[data-test="radio-select-hasura-default"]').should('be.visible');
|
||||
cy.get('[data-test="radio-select-graphql-default"]').should('be.visible');
|
||||
cy.get('label[for="radio-select-graphql-default"]').click();
|
||||
});
|
||||
|
||||
cy.log('**--- Fill Root Fields Customizations**');
|
||||
cy.get('form[aria-label="rootFields"]').within(() => {
|
||||
cy.findByPlaceholderText('Namespace...').type('name_space');
|
||||
cy.findByPlaceholderText('prefix_').type('prefix_');
|
||||
cy.findByPlaceholderText('_suffix').type('_suffix');
|
||||
});
|
||||
|
||||
cy.log('**--- Fill Type Names Customizations**');
|
||||
cy.get('form[aria-label="typeNames"]').within(() => {
|
||||
cy.findByPlaceholderText('prefix_').type('prefix_');
|
||||
cy.findByPlaceholderText('_suffix').type('_suffix');
|
||||
});
|
||||
};
|
||||
|
||||
export const fillDetailsForPgDbUrlForm = (dbName: string) => {
|
||||
cy.log('**--- Fill Form using db url**');
|
||||
cy.getBySel('database-display-name').type(dbName);
|
||||
cy.getBySel('database-type').select('postgres');
|
||||
cy.getBySel('database-url').type(dbUrl);
|
||||
cy.getBySel('max-connections').type('50');
|
||||
cy.getBySel('idle-timeout').type('180');
|
||||
cy.getBySel('retries').type('1');
|
||||
graphqlCustomizationTest();
|
||||
};
|
||||
|
||||
export const fillDetailsForPgConnParamsForm = (dbName: string) => {
|
||||
cy.log('**--- Fill Form using connection parameter**');
|
||||
cy.get("input[type='radio']").eq(2).click();
|
||||
cy.getBySel('database-display-name').type(dbName);
|
||||
cy.getBySel('database-type').select('postgres');
|
||||
cy.getBySel('host').type(config.host);
|
||||
cy.getBySel('port').type(config.port);
|
||||
cy.getBySel('username').type(config.username);
|
||||
cy.getBySel('password').type(config.password);
|
||||
cy.getBySel('database-name').type(config.dbName);
|
||||
graphqlCustomizationTest();
|
||||
};
|
||||
|
||||
export const fillDetailsForPgEnvVarForm = (dbName: string) => {
|
||||
cy.log('**--- Fill Form using env vars**');
|
||||
cy.get("input[type='radio']").eq(0).click();
|
||||
cy.getBySel('database-display-name').type(dbName);
|
||||
cy.getBySel('database-type').select('postgres');
|
||||
cy.getBySel('database-url-env').type('HASURA_GRAPHQL_DATABASE_URL');
|
||||
graphqlCustomizationTest();
|
||||
};
|
||||
|
||||
export const createDB = (dbName: string) => {
|
||||
const postBody = {
|
||||
type: 'pg_add_source',
|
||||
args: {
|
||||
name: dbName,
|
||||
configuration: {
|
||||
connection_info: {
|
||||
database_url: dbUrl,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
cy.request('POST', 'http://localhost:8080/v1/metadata', postBody).then(
|
||||
response => {
|
||||
expect(response.body).to.have.property('message', 'success'); // true
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const removeDB = (dbName: string) => {
|
||||
const postBody = { type: 'pg_drop_source', args: { name: dbName } };
|
||||
cy.request('POST', 'http://localhost:8080/v1/metadata', postBody).then(
|
||||
response => {
|
||||
expect(response.body).to.have.property('message', 'success'); // true
|
||||
}
|
||||
);
|
||||
cy.reload();
|
||||
};
|
||||
|
||||
const createTable = (tableName: string) => {
|
||||
const postBody = {
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
source: 'default',
|
||||
sql: `CREATE TABLE "public"."${tableName}" ("id" serial NOT NULL, "name" text NOT NULL, "countryCode" text DEFAULT 'IN', PRIMARY KEY ("id") );`,
|
||||
cascade: false,
|
||||
read_only: false,
|
||||
},
|
||||
};
|
||||
cy.request('POST', 'http://localhost:8080/v2/query', postBody).then(
|
||||
response => {
|
||||
expect(response.body).to.have.property('result_type', 'CommandOk'); // true
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const trackTable = (tableName: string) => {
|
||||
const postBody = {
|
||||
type: 'pg_track_table',
|
||||
args: {
|
||||
table: {
|
||||
name: tableName,
|
||||
schema: 'public',
|
||||
},
|
||||
source: 'default',
|
||||
},
|
||||
};
|
||||
cy.request('POST', 'http://localhost:8080/v1/metadata', postBody).then(
|
||||
response => {
|
||||
expect(response.body).to.have.property('message', 'success'); // true
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const untrackTable = (tableName: string) => {
|
||||
const postBody = {
|
||||
type: 'pg_untrack_table',
|
||||
args: {
|
||||
table: {
|
||||
schema: 'public',
|
||||
name: tableName,
|
||||
},
|
||||
source: 'default',
|
||||
},
|
||||
};
|
||||
cy.request('POST', 'http://localhost:8080/v1/metadata', postBody).then(
|
||||
response => {
|
||||
expect(response.body).to.have.property('message', 'success'); // true
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const deleteTable = (tableName: string) => {
|
||||
const postBody = {
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
source: 'default',
|
||||
sql: `DROP table "public"."${tableName}";`,
|
||||
cascade: false,
|
||||
read_only: false,
|
||||
},
|
||||
};
|
||||
cy.request('POST', 'http://localhost:8080/v2/query', postBody).then(
|
||||
response => {
|
||||
expect(response.body).to.have.property('result_type', 'CommandOk'); // true
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const createRemoteSchema = (remoteSchemaName: string) => {
|
||||
const postBody = {
|
||||
type: 'add_remote_schema',
|
||||
args: {
|
||||
name: remoteSchemaName,
|
||||
definition: {
|
||||
timeout_seconds: 60,
|
||||
forward_client_headers: false,
|
||||
headers: [],
|
||||
url: 'https://countries.trevorblades.com/',
|
||||
},
|
||||
comment: '',
|
||||
},
|
||||
};
|
||||
cy.request('POST', 'http://localhost:8080/v1/metadata', postBody).then(
|
||||
response => {
|
||||
expect(response.body).to.have.property('message', 'success'); // true
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const deleteRemoteSchema = (remoteSchemaName: string) => {
|
||||
const postBody = {
|
||||
type: 'remove_remote_schema',
|
||||
args: {
|
||||
name: remoteSchemaName,
|
||||
},
|
||||
};
|
||||
cy.request('POST', 'http://localhost:8080/v1/metadata', postBody).then(
|
||||
response => {
|
||||
expect(response.body).to.have.property('message', 'success'); // true
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
type DriverSpec = {
|
||||
name: 'postgres';
|
||||
helpers: {
|
||||
createDB: (dbName: string) => void;
|
||||
removeDB: (dbName: string) => void;
|
||||
createTable: (tableName: string) => void;
|
||||
createRemoteSchema: (remoteSchemaName: string) => void;
|
||||
deleteRemoteSchema: (remoteSchemaName: string) => void;
|
||||
deleteTable: (tableName: string) => void;
|
||||
trackTable: (tableName: string) => void;
|
||||
untrackTable: (tableName: string) => void;
|
||||
};
|
||||
};
|
||||
|
||||
const postgres: DriverSpec = {
|
||||
name: 'postgres',
|
||||
helpers: {
|
||||
createDB,
|
||||
removeDB,
|
||||
createTable,
|
||||
createRemoteSchema,
|
||||
deleteRemoteSchema,
|
||||
deleteTable,
|
||||
trackTable,
|
||||
untrackTable,
|
||||
},
|
||||
};
|
||||
|
||||
export { postgres };
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,424 +0,0 @@
|
||||
import {
|
||||
getElementFromAlias,
|
||||
baseUrl,
|
||||
tableColumnTypeSelector,
|
||||
getIndexRoute,
|
||||
} from '../../../helpers/dataHelpers';
|
||||
|
||||
import {
|
||||
setMetaData,
|
||||
validateCT,
|
||||
createView,
|
||||
validateColumn,
|
||||
validateView,
|
||||
ResultType,
|
||||
TableFields,
|
||||
} from '../../validators/validators';
|
||||
import { setPromptValue } from '../../../helpers/common';
|
||||
|
||||
const userId = 5555;
|
||||
|
||||
export const createTable = (name: string, dict: TableFields) => {
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public/table/add`);
|
||||
cy.get(getElementFromAlias('tableName')).type(`${name}_table_vt`);
|
||||
const keys = Object.keys(dict).map(k => k);
|
||||
const values = Object.keys(dict).map(k => dict[k]);
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
cy.get(getElementFromAlias(`column-${i}`)).type(keys[i]);
|
||||
tableColumnTypeSelector(`col-type-${i}`);
|
||||
cy.get(getElementFromAlias(`data_test_column_type_value_${values[i]}`))
|
||||
.first()
|
||||
.click();
|
||||
}
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('id');
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(7000);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${name}_table_vt/modify`
|
||||
);
|
||||
|
||||
validateCT(`${name}_table_vt`, ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const passVCreateTables = () => {
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
createTable('author', { id: 'integer', name: 'text' });
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
createTable('article', {
|
||||
id: 'integer',
|
||||
title: 'text',
|
||||
Content: 'text',
|
||||
author_id: 'integer',
|
||||
rating: 'integer',
|
||||
});
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
createTable('comment', {
|
||||
id: 'integer',
|
||||
user_id: 'integer',
|
||||
article_id: 'integer',
|
||||
comment: 'text',
|
||||
});
|
||||
};
|
||||
|
||||
export const passVCreateMaterializedViews = () => {
|
||||
createView(`CREATE MATERIALIZED VIEW author_average_rating_vt AS
|
||||
SELECT author_table_vt.id, avg(article_table_vt.rating)
|
||||
From author_table_vt, article_table_vt
|
||||
WHERE author_table_vt.id = article_table_vt.author_id
|
||||
GROUP BY author_table_vt.id`);
|
||||
};
|
||||
|
||||
export const passTrackTable = () => {
|
||||
cy.visit('/data/default/schema/public/');
|
||||
cy.wait(7000);
|
||||
cy.get(
|
||||
getElementFromAlias('add-track-table-author_average_rating_vt')
|
||||
).click();
|
||||
cy.wait(7000);
|
||||
validateView('author_average_rating_vt', ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const passMaterializedViewRoute = () => {
|
||||
cy.get(getElementFromAlias('author_average_rating_vt')).click();
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/views/author_average_rating_vt/browse`
|
||||
);
|
||||
};
|
||||
|
||||
export const passVAddDataarticle = (
|
||||
data: (string | number)[],
|
||||
index: number
|
||||
) => {
|
||||
// Click the Insert Again button.
|
||||
cy.get('label')
|
||||
.contains('id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label').contains('id').next().find('input').last().type(`${data[0]}`);
|
||||
cy.get('label')
|
||||
.contains('title')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('title')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[1]}`);
|
||||
cy.get('label')
|
||||
.contains('Content')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('Content')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[2]}`);
|
||||
cy.get('label')
|
||||
.contains('author_id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('author_id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[3]}`);
|
||||
cy.get('label')
|
||||
.contains('rating')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('rating')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[4]}`);
|
||||
if (index) {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
} else {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
}
|
||||
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const passVAddDataauthor = (
|
||||
data: (string | number)[],
|
||||
index: number
|
||||
) => {
|
||||
cy.get('label')
|
||||
.contains('id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label').contains('id').next().find('input').last().type(`${data[0]}`);
|
||||
cy.get('label')
|
||||
.contains('name')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('name')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[1]}`);
|
||||
if (index) {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
} else {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
}
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const passVAddDatacomment = (
|
||||
data: (string | number)[],
|
||||
index: number
|
||||
) => {
|
||||
cy.get('label')
|
||||
.contains('id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label').contains('id').next().find('input').last().type(`${data[0]}`);
|
||||
cy.get('label')
|
||||
.contains('user_id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('user_id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[1]}`);
|
||||
cy.get('label')
|
||||
.contains('article_id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('article_id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[2]}`);
|
||||
cy.get('label')
|
||||
.contains('comment')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('comment')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[3]}`);
|
||||
if (index) {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
} else {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
}
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
const checkQuerySuccess = () => {
|
||||
// Expect only 4 rows i.e. expect fifth element to not exist
|
||||
cy.get('[role=gridcell]').contains(userId);
|
||||
cy.get('[role=row]').eq(2).should('not.exist');
|
||||
};
|
||||
|
||||
export const passVAddData = () => {
|
||||
let data;
|
||||
cy.get(getElementFromAlias('article_table_vt')).click();
|
||||
cy.get(getElementFromAlias('table-insert-rows')).click();
|
||||
data = [1, 'A', 'Sontent', userId, 4];
|
||||
passVAddDataarticle(data, 0);
|
||||
data = [2, 'B', 'Sontenta', 2, 4];
|
||||
passVAddDataarticle(data, 1);
|
||||
data = [3, 'C', 'Sontentb', userId, 4];
|
||||
passVAddDataarticle(data, 2);
|
||||
cy.get(getElementFromAlias('author_table_vt')).click();
|
||||
cy.get(getElementFromAlias('table-insert-rows')).click();
|
||||
|
||||
data = [userId, 'A'];
|
||||
passVAddDataauthor(data, 0);
|
||||
data = [2, 'B'];
|
||||
passVAddDataauthor(data, 1);
|
||||
cy.get(getElementFromAlias('comment_table_vt')).click();
|
||||
cy.get(getElementFromAlias('table-insert-rows')).click();
|
||||
|
||||
data = [1, 1, 1, 'new comment'];
|
||||
passVAddDatacomment(data, 0);
|
||||
data = [2, 2, 2, 'new comment'];
|
||||
passVAddDatacomment(data, 1);
|
||||
data = [3, 1, 2, 'new comment'];
|
||||
passVAddDatacomment(data, 2);
|
||||
};
|
||||
|
||||
export const passVFilterQueryEq = () => {
|
||||
// Select column with type `text`
|
||||
cy.get('select')
|
||||
.find('option')
|
||||
.contains('-- column --')
|
||||
.parent()
|
||||
.first()
|
||||
.select('id');
|
||||
// Type value as `filter-text`
|
||||
cy.get("input[placeholder='-- value --']").last().type(`${userId}`);
|
||||
// Run query
|
||||
cy.get(getElementFromAlias('run-query')).click();
|
||||
cy.wait(5000);
|
||||
// Check if the query was successful
|
||||
checkQuerySuccess();
|
||||
};
|
||||
|
||||
const checkOrder = (order: string) => {
|
||||
// Utility function to get right element
|
||||
if (order === 'asc') {
|
||||
cy.get('[role=row]').each(($el, index) => {
|
||||
if (index === 1) {
|
||||
cy.wrap($el).find('[role=gridcell]').first().next().next().contains(2);
|
||||
}
|
||||
if (index === 2) {
|
||||
cy.wrap($el)
|
||||
.find('[role=gridcell]')
|
||||
.first()
|
||||
.next()
|
||||
.next()
|
||||
.contains(userId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cy.get('[role=row]').each(($el, index) => {
|
||||
if (index === 2) {
|
||||
cy.wrap($el).find('[role=gridcell]').first().next().next().contains(2);
|
||||
}
|
||||
if (index === 1) {
|
||||
cy.wrap($el)
|
||||
.find('[role=gridcell]')
|
||||
.first()
|
||||
.next()
|
||||
.next()
|
||||
.contains(userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const passVAscendingSort = () => {
|
||||
cy.wait(7000);
|
||||
// cy.scrollTo('top');
|
||||
// Select column with type 'serial'
|
||||
cy.get('select')
|
||||
.find('option')
|
||||
.contains('-- column --')
|
||||
.parent()
|
||||
.last()
|
||||
.select('id');
|
||||
// Run query
|
||||
cy.get(getElementFromAlias('run-query')).click();
|
||||
// Check order
|
||||
checkOrder('asc');
|
||||
};
|
||||
|
||||
export const passModifyMaterializedView = () => {
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
cy.get('button').contains('Modify').last().click();
|
||||
cy.url().should('eq', `${baseUrl}/data/sql`);
|
||||
};
|
||||
|
||||
export const passVAddManualObjRel = () => {
|
||||
cy.get(getElementFromAlias('author_average_rating_vt')).click();
|
||||
cy.wait(2000);
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.wait(2000);
|
||||
cy.get(getElementFromAlias('create-edit-manual-rel')).click();
|
||||
cy.get(getElementFromAlias('manual-relationship-type')).select('object');
|
||||
cy.get("input[placeholder='Enter relationship name']").type('author');
|
||||
cy.get(getElementFromAlias('manual-relationship-ref-schema')).select(
|
||||
'public'
|
||||
);
|
||||
cy.get(getElementFromAlias('manual-relationship-ref-table')).select(
|
||||
'author_table_vt'
|
||||
);
|
||||
cy.get(getElementFromAlias('manual-relationship-lcol-0')).select('id');
|
||||
cy.get(getElementFromAlias('manual-relationship-rcol-0')).select('id');
|
||||
cy.get(getElementFromAlias('create-manual-rel-save')).click();
|
||||
cy.wait(7000);
|
||||
validateColumn(
|
||||
'author_average_rating_vt',
|
||||
['avg', { name: 'author', columns: ['name'] }],
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
};
|
||||
|
||||
export const passVDeleteRelationships = () => {
|
||||
cy.get(getElementFromAlias('author_average_rating_vt')).click();
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.get(getElementFromAlias('relationship-toggle-editor-author')).click();
|
||||
cy.get(getElementFromAlias('relationship-remove-author')).click();
|
||||
cy.on('window:alert', str => {
|
||||
return expect(str === 'Are you sure?').to.be.true;
|
||||
});
|
||||
cy.wait(7000);
|
||||
validateColumn(
|
||||
'author_average_rating_vt',
|
||||
['avg', { name: 'author', columns: ['name'] }],
|
||||
ResultType.FAILURE
|
||||
);
|
||||
};
|
||||
|
||||
export const passVDeleteMaterializedView = () => {
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
setPromptValue('author_average_rating_vt');
|
||||
cy.get(getElementFromAlias('delete-view')).click();
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(7000);
|
||||
validateView('author_average_rating_vt', ResultType.FAILURE);
|
||||
};
|
||||
|
||||
export const deleteTable = (name: string) => {
|
||||
cy.get(getElementFromAlias(name)).click();
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
setPromptValue(name);
|
||||
cy.get(getElementFromAlias('delete-table')).click();
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(7000);
|
||||
validateCT(name, ResultType.FAILURE);
|
||||
cy.wait(7000);
|
||||
};
|
||||
|
||||
export const passVDeleteTables = () => {
|
||||
deleteTable('comment_table_vt');
|
||||
deleteTable('article_table_vt');
|
||||
deleteTable('author_table_vt');
|
||||
};
|
||||
|
||||
export const setValidationMetaData = () => {
|
||||
setMetaData();
|
||||
};
|
@ -1,54 +0,0 @@
|
||||
import {
|
||||
passVCreateTables,
|
||||
passVCreateMaterializedViews,
|
||||
passVAddData,
|
||||
passTrackTable,
|
||||
passVAddManualObjRel,
|
||||
passVAscendingSort,
|
||||
passModifyMaterializedView,
|
||||
passVFilterQueryEq,
|
||||
passMaterializedViewRoute,
|
||||
passVDeleteRelationships,
|
||||
passVDeleteMaterializedView,
|
||||
passVDeleteTables,
|
||||
} from './spec';
|
||||
import { testMode } from '../../../helpers/common';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
// Visit the index route
|
||||
cy.visit(getIndexRoute());
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runMaterializedViewsTest = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Materialized Views', () => {
|
||||
it('Create Tables', passVCreateTables);
|
||||
it('Add data to table', passVAddData);
|
||||
it('Create MaterializedView', passVCreateMaterializedViews);
|
||||
it('Adding it to the table', passTrackTable);
|
||||
it('Check the materializedview route', passMaterializedViewRoute);
|
||||
it('Ascending order MaterializedView Table', passVAscendingSort);
|
||||
it('Filter the MaterializedView table', passVFilterQueryEq);
|
||||
it('Modify the View', passModifyMaterializedView);
|
||||
it('Adding Object Relationship to MaterializedView', passVAddManualObjRel);
|
||||
it('Deleting Relationship', passVDeleteRelationships);
|
||||
it('Deleting MaterializedView', passVDeleteMaterializedView);
|
||||
it('Deleting Tables', passVDeleteTables);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runMaterializedViewsTest();
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
import { validateMigrationMode } from '../../validators/validators';
|
||||
|
||||
import { toggleOnMigrationMode, toggleOffMigrationMode } from './utils';
|
||||
|
||||
export const testToggleButton = () => {
|
||||
// Turn off migration mode
|
||||
toggleOffMigrationMode();
|
||||
cy.wait(10000);
|
||||
// Validate
|
||||
validateMigrationMode(false);
|
||||
cy.wait(7000);
|
||||
// Turn on migration mode
|
||||
toggleOnMigrationMode();
|
||||
cy.wait(10000);
|
||||
// Validate
|
||||
validateMigrationMode(true);
|
||||
cy.wait(7000);
|
||||
};
|
||||
|
||||
export const checkToggleButton = () => {
|
||||
cy.window().then(win => {
|
||||
const { consoleMode } = win.__env;
|
||||
if (consoleMode === 'cli') {
|
||||
testToggleButton();
|
||||
} else {
|
||||
cy.get('[class=react-toggle-track]').should('not.exist');
|
||||
}
|
||||
});
|
||||
};
|
@ -1,27 +0,0 @@
|
||||
import { checkToggleButton } from './spec';
|
||||
import { testMode } from '../../../helpers/common';
|
||||
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
describe('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
// Visit the index route
|
||||
cy.visit(getIndexRoute());
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runMigrationModeTests = () => {
|
||||
describe('Migration mode', () => {
|
||||
it('Check the toggle button', checkToggleButton);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runMigrationModeTests();
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
import { migrateModeUrl } from '../../../helpers/common';
|
||||
|
||||
export const toggleOnMigrationMode = () => {
|
||||
cy.request({
|
||||
method: 'GET',
|
||||
url: migrateModeUrl,
|
||||
}).then(response => {
|
||||
if (response.body.migration_mode === 'false') {
|
||||
// Go to migrations section
|
||||
cy.get('a')
|
||||
.contains('Migrations')
|
||||
.click();
|
||||
cy.wait(3000);
|
||||
// Toggle Migration mode
|
||||
cy.get('[class=react-toggle-track]').click();
|
||||
cy.wait(10000);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const toggleOffMigrationMode = () => {
|
||||
cy.request({
|
||||
method: 'GET',
|
||||
url: migrateModeUrl,
|
||||
}).then(response => {
|
||||
if (response.body.migration_mode === 'true') {
|
||||
// Go to migrations section
|
||||
cy.get('a')
|
||||
.contains('Migrations')
|
||||
.click();
|
||||
cy.wait(3000);
|
||||
// Toggle Migration mode
|
||||
cy.get('[class=react-toggle-track]').click();
|
||||
cy.wait(10000);
|
||||
}
|
||||
});
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,439 +0,0 @@
|
||||
import {
|
||||
tableColumnTypeSelector,
|
||||
baseUrl,
|
||||
getTableName,
|
||||
getColName,
|
||||
getElementFromAlias,
|
||||
createUntrackedFunctionSQL,
|
||||
dropUntrackedFunctionSQL,
|
||||
getIndexRoute,
|
||||
} from '../../../helpers/dataHelpers';
|
||||
|
||||
import {
|
||||
setMetaData,
|
||||
validateCT,
|
||||
validateColumn,
|
||||
ResultType,
|
||||
dataRequest,
|
||||
} from '../../validators/validators';
|
||||
import { setPromptValue } from '../../../helpers/common';
|
||||
|
||||
const testName = 'mod';
|
||||
|
||||
export const passMTFunctionList = () => {
|
||||
const tableName = getTableName(0, testName);
|
||||
dataRequest(
|
||||
createUntrackedFunctionSQL(`${tableName}_id_fn`, tableName),
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('modify-table-edit-computed-field-0')).click();
|
||||
|
||||
cy.get(getElementFromAlias('functions-dropdown')).click();
|
||||
|
||||
cy.get('[data-test^="data_test_column_type_value_"]').should(
|
||||
'have.length',
|
||||
1
|
||||
);
|
||||
|
||||
cy.get('[data-test^="data_test_column_type_value_"]')
|
||||
.first()
|
||||
.should('have.text', `${getTableName(0, testName)}_id_fn`.toLowerCase());
|
||||
dataRequest(
|
||||
dropUntrackedFunctionSQL(`${tableName}_id_fn`),
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
};
|
||||
|
||||
export const passMTCreateTable = () => {
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public/table/add`);
|
||||
cy.get(getElementFromAlias('tableName')).type(getTableName(0, testName));
|
||||
cy.get(getElementFromAlias('column-0')).type('id');
|
||||
tableColumnTypeSelector('col-type-0');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
|
||||
.first()
|
||||
.click();
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('id');
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(7000);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
validateCT(getTableName(0, testName), ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const passMTCheckRoute = () => {
|
||||
// Click on the create table button
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
// Match the URL
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
};
|
||||
|
||||
export const passMTRenameTable = () => {
|
||||
cy.get(getElementFromAlias('heading-edit-table')).click();
|
||||
cy.get(getElementFromAlias('heading-edit-table-input'))
|
||||
.clear()
|
||||
.type(getTableName(3, testName));
|
||||
cy.get(getElementFromAlias('heading-edit-table-save')).click();
|
||||
cy.wait(15000);
|
||||
validateCT(getTableName(3, testName), ResultType.SUCCESS);
|
||||
cy.get(getElementFromAlias('heading-edit-table')).click();
|
||||
cy.get(getElementFromAlias('heading-edit-table-input'))
|
||||
.clear()
|
||||
.type(getTableName(0, testName));
|
||||
cy.get(getElementFromAlias('heading-edit-table-save')).click();
|
||||
cy.wait(15000);
|
||||
validateCT(getTableName(0, testName), ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const passMTRenameColumn = () => {
|
||||
cy.wait(10000);
|
||||
cy.get(getElementFromAlias('modify-table-edit-column-0')).click();
|
||||
cy.get(getElementFromAlias('edit-col-name')).clear().type(getColName(3));
|
||||
cy.get(getElementFromAlias('modify-table-column-0-save')).click();
|
||||
cy.wait(15000);
|
||||
validateColumn(
|
||||
getTableName(0, testName),
|
||||
[getColName(3)],
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
cy.get(getElementFromAlias('modify-table-edit-column-0')).click();
|
||||
cy.get(getElementFromAlias('edit-col-name')).clear().type('id');
|
||||
cy.get(getElementFromAlias('modify-table-column-0-save')).click();
|
||||
cy.wait(15000);
|
||||
validateColumn(getTableName(0, testName), ['id'], ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const passMTChangeDefaultValueForPKey = () => {
|
||||
cy.wait(10000);
|
||||
cy.get(getElementFromAlias('modify-table-edit-column-0')).click();
|
||||
cy.get(getElementFromAlias('edit-col-default')).clear().type('1234');
|
||||
cy.get(getElementFromAlias('modify-table-column-0-save')).click();
|
||||
cy.wait(15000);
|
||||
};
|
||||
|
||||
export const passMTMoveToTable = () => {
|
||||
cy.get(getElementFromAlias(getTableName(0, testName))).click();
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/browse`
|
||||
);
|
||||
};
|
||||
|
||||
export const failMTWithoutColName = () => {
|
||||
cy.get(getElementFromAlias('modify-table-edit-add-new-column')).click();
|
||||
cy.get(getElementFromAlias('modify-table-add-new-column-save')).click();
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
|
||||
validateColumn(
|
||||
getTableName(0, testName),
|
||||
[getColName(2)],
|
||||
ResultType.FAILURE
|
||||
);
|
||||
};
|
||||
|
||||
export const failMTWithoutColType = () => {
|
||||
cy.get(getElementFromAlias('column-name')).type(getColName(2));
|
||||
cy.get(getElementFromAlias('modify-table-add-new-column-save')).click();
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
validateColumn(
|
||||
getTableName(0, testName),
|
||||
[getColName(2)],
|
||||
ResultType.FAILURE
|
||||
);
|
||||
};
|
||||
|
||||
export const Addcolumnnullable = () => {
|
||||
cy.get(getElementFromAlias('column-name')).type('{selectall}{del}');
|
||||
cy.get(getElementFromAlias('column-name')).type(getColName(3));
|
||||
tableColumnTypeSelector('col-type-0');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_text'))
|
||||
.first()
|
||||
.click();
|
||||
cy.get(getElementFromAlias('nullable-checkbox')).uncheck({ force: true });
|
||||
cy.get(getElementFromAlias('modify-table-add-new-column-save')).click();
|
||||
cy.wait(2500);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
validateColumn(
|
||||
getTableName(0, testName),
|
||||
[getColName(3)],
|
||||
ResultType.FAILURE
|
||||
);
|
||||
};
|
||||
|
||||
export const Addcolumnname = (name: string) => {
|
||||
cy.get(getElementFromAlias('column-name')).type('{selectall}{del}');
|
||||
cy.get(getElementFromAlias('column-name')).type(name);
|
||||
|
||||
tableColumnTypeSelector('col-type-0');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
|
||||
.first()
|
||||
.click();
|
||||
|
||||
cy.get(getElementFromAlias('modify-table-add-new-column-save')).click();
|
||||
cy.wait(5000);
|
||||
validateColumn(getTableName(0, testName), [name], ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const passMTAddColumn = () => {
|
||||
cy.get(getElementFromAlias('frequently-used-columns')).first().should('exist');
|
||||
cy.get(getElementFromAlias('column-name')).type('{selectall}{del}');
|
||||
cy.get(getElementFromAlias('column-name')).type(getColName(0));
|
||||
tableColumnTypeSelector('col-type-0');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
|
||||
.first()
|
||||
.click();
|
||||
cy.get(getElementFromAlias('modify-table-add-new-column-save')).click();
|
||||
cy.wait(5000);
|
||||
validateColumn(
|
||||
getTableName(0, testName),
|
||||
[getColName(0)],
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
};
|
||||
|
||||
export const Movetocolumn = () => {
|
||||
Addcolumnname(getColName(1));
|
||||
cy.get(getElementFromAlias('modify-table-edit-column-1')).click();
|
||||
};
|
||||
|
||||
export const failMCWithWrongDefaultValue = () => {
|
||||
cy.get(getElementFromAlias('modify-table-edit-column-1')).click();
|
||||
cy.get(getElementFromAlias('edit-col-default')).type('abcd');
|
||||
cy.get(getElementFromAlias('modify-table-column-1-save')).click();
|
||||
};
|
||||
|
||||
export const passMCWithRightDefaultValue = () => {
|
||||
cy.get(getElementFromAlias('edit-col-default')).clear().type('1234');
|
||||
cy.get(getElementFromAlias('modify-table-column-1-save')).click();
|
||||
cy.wait(10000);
|
||||
};
|
||||
|
||||
export const passCreateForeignKey = () => {
|
||||
cy.get(getElementFromAlias('modify-table-edit-fk-0')).click();
|
||||
cy.get(getElementFromAlias('foreign-key-ref-table-0')).select(
|
||||
getTableName(0, testName)
|
||||
);
|
||||
cy.get(getElementFromAlias('foreign-key-0-lcol-0')).select('0');
|
||||
cy.get(getElementFromAlias('foreign-key-0-rcol-0')).select('id');
|
||||
cy.get(getElementFromAlias('modify-table-fk-0-save')).click();
|
||||
cy.wait(10000);
|
||||
};
|
||||
|
||||
export const passRemoveForeignKey = () => {
|
||||
cy.get(getElementFromAlias('modify-table-edit-fk-0')).click();
|
||||
cy.get(getElementFromAlias('modify-table-fk-0-remove')).click();
|
||||
cy.wait(10000);
|
||||
};
|
||||
|
||||
export const passModifyPkey = () => {
|
||||
cy.get(getElementFromAlias('modify-table-edit-pks')).click();
|
||||
cy.get(getElementFromAlias('primary-key-select-1')).select('1');
|
||||
cy.get(getElementFromAlias('modify-table-pks-save')).click();
|
||||
cy.get(getElementFromAlias('pk-config-text')).within(() => {
|
||||
cy.get('b').contains(getColName(0));
|
||||
cy.get('b').contains('id');
|
||||
});
|
||||
cy.wait(5000);
|
||||
|
||||
cy.get(getElementFromAlias('remove-pk-column-1')).click();
|
||||
cy.get(getElementFromAlias('modify-table-pks-save')).click();
|
||||
cy.get(getElementFromAlias('pk-config-text')).within(() => {
|
||||
cy.get('b').contains('id');
|
||||
});
|
||||
cy.get(getElementFromAlias('pk-config-text')).within(() => {
|
||||
cy.get('b').should('not.contain', getColName(0));
|
||||
});
|
||||
cy.get(getElementFromAlias('modify-table-close-pks')).click();
|
||||
cy.wait(3000);
|
||||
};
|
||||
|
||||
export const passCreateUniqueKey = () => {
|
||||
cy.get(getElementFromAlias('modify-table-edit-unique-key-0')).click();
|
||||
cy.get(getElementFromAlias('unique-key-0-column-0')).select('0');
|
||||
cy.get(getElementFromAlias('unique-key-0-column-1')).select('1');
|
||||
cy.wait(1000);
|
||||
cy.get(getElementFromAlias('modify-table-unique-key-0-save')).click();
|
||||
cy.wait(5000);
|
||||
cy.get('div').contains(
|
||||
`${getTableName(0, testName)}_id_${getColName(0)}_key`
|
||||
);
|
||||
};
|
||||
|
||||
export const passModifyUniqueKey = () => {
|
||||
cy.get(getElementFromAlias('modify-table-edit-unique-key-0')).click();
|
||||
cy.get(getElementFromAlias('remove-uk-0-column-0')).click();
|
||||
cy.get(getElementFromAlias('modify-table-unique-key-0-save')).click();
|
||||
cy.wait(5000);
|
||||
cy.get('div').contains(`${getTableName(0, testName)}_${getColName(0)}_key`);
|
||||
};
|
||||
|
||||
export const passRemoveUniqueKey = () => {
|
||||
cy.get(getElementFromAlias('modify-table-edit-unique-key-0')).click();
|
||||
cy.get(getElementFromAlias('modify-table-unique-key-0-remove')).click();
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const passMTDeleteCol = () => {
|
||||
setPromptValue(getColName(0));
|
||||
cy.get(getElementFromAlias('modify-table-edit-column-1')).click();
|
||||
cy.get(getElementFromAlias('modify-table-column-1-remove')).click();
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(5000);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
validateColumn(
|
||||
getTableName(0, testName),
|
||||
[getColName(0)],
|
||||
ResultType.FAILURE
|
||||
);
|
||||
};
|
||||
|
||||
export const passMTDeleteTableCancel = () => {
|
||||
setPromptValue(null);
|
||||
cy.get(getElementFromAlias('delete-table')).click();
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
|
||||
validateCT(getTableName(0, testName), ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const passMTDeleteTable = () => {
|
||||
setPromptValue(getTableName(0, testName));
|
||||
cy.get(getElementFromAlias('delete-table')).click();
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(5000);
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public`);
|
||||
validateCT(getTableName(0, testName), ResultType.FAILURE);
|
||||
};
|
||||
|
||||
export const setValidationMetaData = () => {
|
||||
setMetaData();
|
||||
};
|
||||
|
||||
// Views Modify /////////////////////////////////////////////////
|
||||
|
||||
export const createTable = (name: string, dict: { [key: string]: any }) => {
|
||||
cy.url().should('eq', `${baseUrl}/data/schema/public/table/add`);
|
||||
cy.get(getElementFromAlias('tableName')).type(`${name}_table_mod`);
|
||||
const keys = Object.keys(dict).map(k => k);
|
||||
const values = Object.keys(dict).map(k => dict[k]);
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
cy.get('input[placeholder="column_name"]').last().type(keys[i]);
|
||||
cy.get('select')
|
||||
.find('option')
|
||||
.contains('-- type --')
|
||||
.parent()
|
||||
.last()
|
||||
.select(values[i]);
|
||||
}
|
||||
|
||||
cy.get('select').last().select('id');
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(7000);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${name}_table_mod/modify`
|
||||
);
|
||||
|
||||
validateCT(`${name}_table_mod`, ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const Createtables = () => {
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
createTable('author', { id: 'integer', name: 'Text' });
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
createTable('article', {
|
||||
id: 'integer',
|
||||
title: 'text',
|
||||
Content: 'text',
|
||||
author_id: 'integer',
|
||||
rating: 'integer',
|
||||
});
|
||||
};
|
||||
|
||||
export const Createview = () => {
|
||||
cy.get(getElementFromAlias('sql-link')).click();
|
||||
cy.get('textarea').type(`CREATE VIEW author_average_rating_mod AS
|
||||
SELECT author_table_mod.id, avg(article_table.rating)
|
||||
From author_table_mod, article_table_mod
|
||||
WHERE author_table_mod.id = article_table_mod.author_id
|
||||
GROUP BY author_table_mod.id`);
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
validateCT('author_average_rating_mod', ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const Checkviewtable = () => {
|
||||
cy.get(getElementFromAlias('author_average_rating_mod')).click();
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/views/author_average_rating_mod/browse`
|
||||
);
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
cy.get(getElementFromAlias('modify-view')).click();
|
||||
cy.url().should('eq', `${baseUrl}/data/sql`);
|
||||
};
|
||||
|
||||
export const Checkviewtabledelete = () => {
|
||||
cy.get(getElementFromAlias('author_average_rating_mod')).click();
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/views/author_average_rating_mod/browse`
|
||||
);
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
setPromptValue('author_average_rating_mod');
|
||||
cy.get(getElementFromAlias('delete-view')).click();
|
||||
cy.window().its('prompt').should('be.called');
|
||||
|
||||
cy.wait(7000);
|
||||
validateCT('author_average_rating_mod', ResultType.FAILURE);
|
||||
};
|
||||
|
||||
export const Issue = () => {
|
||||
cy.get('.ace_text-input').first().type('#include');
|
||||
};
|
@ -1,75 +0,0 @@
|
||||
import {
|
||||
passMTCheckRoute,
|
||||
passMTMoveToTable,
|
||||
passMTCreateTable,
|
||||
failMTWithoutColName,
|
||||
failMTWithoutColType,
|
||||
passMTAddColumn,
|
||||
passMTDeleteTableCancel,
|
||||
passMTDeleteCol,
|
||||
passMTDeleteTable,
|
||||
passMCWithRightDefaultValue,
|
||||
failMCWithWrongDefaultValue,
|
||||
passCreateForeignKey,
|
||||
passRemoveForeignKey,
|
||||
passMTRenameTable,
|
||||
passMTRenameColumn,
|
||||
passModifyPkey,
|
||||
passCreateUniqueKey,
|
||||
passModifyUniqueKey,
|
||||
passRemoveUniqueKey,
|
||||
passMTChangeDefaultValueForPKey,
|
||||
passMTFunctionList,
|
||||
} from './spec';
|
||||
|
||||
import { testMode } from '../../../helpers/common';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
describe('Check Data Tab', () => {
|
||||
it('Clicking on Data tab opens the correct route', () => {
|
||||
// Visit the index route
|
||||
cy.visit(getIndexRoute());
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runModifyTableTests = () => {
|
||||
describe('Modify Table', () => {
|
||||
it('Creating a table', passMTCreateTable);
|
||||
it('Moving to the table', passMTMoveToTable);
|
||||
it('Modify table button opens the correct route', passMTCheckRoute);
|
||||
it(
|
||||
'Can create computed field with compatible functions',
|
||||
passMTFunctionList
|
||||
);
|
||||
it('Pass renaming table', passMTRenameTable);
|
||||
it('Pass renaming column', passMTRenameColumn);
|
||||
it('Fails to add column without column name', failMTWithoutColName);
|
||||
it('Fails without type selected', failMTWithoutColType);
|
||||
it('Add a column', passMTAddColumn);
|
||||
it('Fail modify with wrong default value', failMCWithWrongDefaultValue);
|
||||
it('Pass modify with wrong default value', passMCWithRightDefaultValue);
|
||||
it('Pass create foreign-key', passCreateForeignKey);
|
||||
it('Pass remove foreign-key', passRemoveForeignKey);
|
||||
it(
|
||||
'Pass edit default value for primary key',
|
||||
passMTChangeDefaultValueForPKey
|
||||
);
|
||||
it('Pass modifying a primary key', passModifyPkey);
|
||||
it('Pass creating a unique key', passCreateUniqueKey);
|
||||
it('Pass modifying a unique key', passModifyUniqueKey);
|
||||
it('Pass removing a unique key', passRemoveUniqueKey);
|
||||
it('Delete the column', passMTDeleteCol);
|
||||
it('Delete Table Cancel', passMTDeleteTableCancel);
|
||||
it('Delete table', passMTDeleteTable);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runModifyTableTests();
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
import {
|
||||
tableColumnTypeSelector,
|
||||
baseUrl,
|
||||
getTableName,
|
||||
getElementFromAlias,
|
||||
getColName,
|
||||
queryTypes,
|
||||
} from '../../../helpers/dataHelpers';
|
||||
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
|
||||
import { testPermissions, permRemove, createView, trackView } from './utils';
|
||||
import { setPromptValue } from '../../../helpers/common';
|
||||
|
||||
const testName = 'perm';
|
||||
|
||||
export const passPTCreateTable = () => {
|
||||
// Click on create table
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
// Match the URL
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public/table/add`);
|
||||
// Type table name
|
||||
cy.get(getElementFromAlias('tableName')).type(getTableName(0, testName));
|
||||
// Set first column
|
||||
cy.get(getElementFromAlias('column-0')).type(getColName(0));
|
||||
tableColumnTypeSelector('col-type-0');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_serial'))
|
||||
.first()
|
||||
.click();
|
||||
// Set second column
|
||||
cy.get(getElementFromAlias('column-1')).type(getColName(1));
|
||||
tableColumnTypeSelector('col-type-1');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_integer'))
|
||||
.first()
|
||||
.click();
|
||||
|
||||
// Set third column
|
||||
cy.get(getElementFromAlias('column-2')).type(getColName(2));
|
||||
tableColumnTypeSelector('col-type-2');
|
||||
cy.get(getElementFromAlias('data_test_column_type_value_text'))
|
||||
.first()
|
||||
.click();
|
||||
// Set primary key
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('0');
|
||||
// Create
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(7000);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(0, testName)}/modify`
|
||||
);
|
||||
};
|
||||
|
||||
export const passPTCheckRoute = () => {
|
||||
// Go to permissiosn tab
|
||||
cy.get(getElementFromAlias('table-permissions')).click();
|
||||
// Match the URL
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/permissions`
|
||||
);
|
||||
};
|
||||
|
||||
export const passPTNoChecks = () => {
|
||||
// Type role
|
||||
cy.get(getElementFromAlias('role-textbox')).type('role0');
|
||||
// Set permissions
|
||||
testPermissions(getTableName(0, testName), 'none');
|
||||
};
|
||||
|
||||
export const passPTCustomChecks = () => {
|
||||
testPermissions(getTableName(0, testName), 'custom');
|
||||
};
|
||||
|
||||
export const passPTRemovePerms = () => {
|
||||
queryTypes.forEach(query => {
|
||||
permRemove(getTableName(0, testName), query);
|
||||
});
|
||||
};
|
||||
|
||||
export const passPVCreateView = () => {
|
||||
// create a view
|
||||
createView(getTableName(1, testName), getTableName(0, testName));
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const passPVPermissions = () => {
|
||||
// Track the view
|
||||
trackView();
|
||||
// Type role
|
||||
cy.get(getElementFromAlias('role-textbox')).type('role0');
|
||||
// Test permissions
|
||||
testPermissions(getTableName(1, testName), 'none', true);
|
||||
testPermissions(getTableName(1, testName), 'custom', true);
|
||||
};
|
||||
|
||||
export const passPVRemovePerms = () => {
|
||||
permRemove(getTableName(1, testName), 'select');
|
||||
};
|
||||
|
||||
export const passPVDeleteView = () => {
|
||||
// Go to modify view
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
// Delete view
|
||||
setPromptValue(getTableName(1, testName));
|
||||
cy.get(getElementFromAlias('delete-view')).click();
|
||||
cy.window()
|
||||
.its('prompt')
|
||||
.should('be.called');
|
||||
cy.wait(7000);
|
||||
};
|
||||
|
||||
export const passPTDeleteTable = () => {
|
||||
// Go to the table
|
||||
cy.get(getElementFromAlias(getTableName(0, testName))).click();
|
||||
cy.wait(7000);
|
||||
// Go to modify table
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
// Delete table
|
||||
setPromptValue(getTableName(0, testName));
|
||||
cy.get(getElementFromAlias('delete-table')).click();
|
||||
cy.window()
|
||||
.its('prompt')
|
||||
.should('be.called');
|
||||
cy.wait(7000);
|
||||
};
|
||||
|
||||
export const setValidationMetaData = () => {
|
||||
setMetaData();
|
||||
};
|
@ -1,50 +0,0 @@
|
||||
import {
|
||||
passPTCreateTable,
|
||||
passPTCheckRoute,
|
||||
passPTNoChecks,
|
||||
passPTCustomChecks,
|
||||
passPTRemovePerms,
|
||||
passPVCreateView,
|
||||
passPVPermissions,
|
||||
passPVRemovePerms,
|
||||
passPVDeleteView,
|
||||
passPTDeleteTable,
|
||||
} from './spec';
|
||||
import { testMode } from '../../../helpers/common';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Check Data Tab', () => {
|
||||
it('Visiting the data URL opens the correct route', () => {
|
||||
// Visit the index route
|
||||
cy.visit(getIndexRoute());
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runPermissionsTests = () => {
|
||||
describe.skip('Permissions', () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
it('Create a table', passPTCreateTable);
|
||||
it('Create a view', passPVCreateView);
|
||||
it('Check permission route', passPTCheckRoute);
|
||||
it('Table No-check permissions work as expected', passPTNoChecks);
|
||||
it('Table Custom-check permissions work as expected', passPTCustomChecks);
|
||||
it('Table Permissions removal works as expected', passPTRemovePerms);
|
||||
it('View permissions work as expected', passPVPermissions);
|
||||
it('View Permissions removal works as expected', passPVRemovePerms);
|
||||
it('Delete the views', passPVDeleteView);
|
||||
it('Delete the test table', passPTDeleteTable);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runPermissionsTests();
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
import {
|
||||
getElementFromAlias,
|
||||
getTableName,
|
||||
getColName,
|
||||
queryTypes,
|
||||
makeDataAPIOptions,
|
||||
} from '../../../helpers/dataHelpers';
|
||||
|
||||
import {
|
||||
validatePermission,
|
||||
QueryType,
|
||||
ResultType,
|
||||
CheckType,
|
||||
} from '../../validators/validators';
|
||||
|
||||
const testName = 'perm';
|
||||
|
||||
export const savePermission = () => {
|
||||
cy.get(getElementFromAlias('Save-Permissions-button')).click();
|
||||
cy.wait(7000);
|
||||
};
|
||||
|
||||
export const permNoCheck = (tableName: string, query: QueryType) => {
|
||||
// click on the query type to edit permission
|
||||
cy.get(getElementFromAlias(`role0-${query}`)).click();
|
||||
cy.get(getElementFromAlias('without-checks')).first().click();
|
||||
// set filter { }
|
||||
// Toggle all columns in case
|
||||
if (query === 'select' || query === 'update') {
|
||||
cy.get(getElementFromAlias('toggle-all-col-btn')).click();
|
||||
}
|
||||
if (query === 'insert' || query === 'update') {
|
||||
cy.get(getElementFromAlias('toggle-presets-permission')).click();
|
||||
cy.get(getElementFromAlias('column-presets-column-0')).select(
|
||||
getColName(0)
|
||||
);
|
||||
cy.get(getElementFromAlias('column-presets-type-0')).select('static');
|
||||
cy.get(getElementFromAlias('column-presets-value-0')).type('1').blur();
|
||||
cy.get(getElementFromAlias('column-presets-column-1')).select(
|
||||
getColName(1)
|
||||
);
|
||||
cy.get(getElementFromAlias('column-presets-type-1')).select('session');
|
||||
cy.get(getElementFromAlias('column-presets-value-1')).type('user-id');
|
||||
}
|
||||
// Save
|
||||
savePermission();
|
||||
// Validate
|
||||
validatePermission(
|
||||
tableName,
|
||||
'role0',
|
||||
query,
|
||||
'none',
|
||||
ResultType.SUCCESS,
|
||||
null
|
||||
);
|
||||
};
|
||||
|
||||
export const permCustomCheck = (tableName: string, query: QueryType) => {
|
||||
// click on the query type to edit permission
|
||||
cy.get(getElementFromAlias(`role0-${query}`)).click();
|
||||
// check the without checks textbox
|
||||
cy.get(getElementFromAlias('toggle-row-permission')).click();
|
||||
cy.get(getElementFromAlias('custom-check')).first().click();
|
||||
|
||||
cy.get(getElementFromAlias('qb_container'))
|
||||
.first()
|
||||
.within(() => {
|
||||
// Select column
|
||||
cy.get(getElementFromAlias('qb-select')).first().select(getColName(0));
|
||||
// Select operator
|
||||
cy.get(getElementFromAlias('qb-select'))
|
||||
.last()
|
||||
.select(`${getColName(0)}._eq`);
|
||||
});
|
||||
// Set filter to 1
|
||||
cy.get(getElementFromAlias('perm-check-textbox')).first().type('1');
|
||||
// Save
|
||||
savePermission();
|
||||
// Validate
|
||||
validatePermission(
|
||||
tableName,
|
||||
'role0',
|
||||
query,
|
||||
'custom',
|
||||
ResultType.SUCCESS,
|
||||
[0, 1, 2].map(i => getColName(i))
|
||||
);
|
||||
// Do not allow users to make upset queries in case of Insert
|
||||
};
|
||||
|
||||
export const permRemove = (tableName: string, query: QueryType) => {
|
||||
// click on the query type to edit permission
|
||||
cy.get(getElementFromAlias(`role0-${query}`)).click();
|
||||
// Remove permission
|
||||
cy.get(getElementFromAlias('Delete-Permissions-button')).click();
|
||||
cy.wait(2500);
|
||||
cy.wait(5000);
|
||||
// Validate
|
||||
validatePermission(
|
||||
tableName,
|
||||
'role0',
|
||||
query,
|
||||
'custom',
|
||||
ResultType.FAILURE,
|
||||
null
|
||||
);
|
||||
};
|
||||
|
||||
export const testPermissions = (
|
||||
tableName: string,
|
||||
check: CheckType,
|
||||
isView?: boolean
|
||||
) => {
|
||||
let allQueryTypes: QueryType[] = queryTypes;
|
||||
if (isView) {
|
||||
allQueryTypes = ['select'];
|
||||
}
|
||||
|
||||
if (check === 'none') {
|
||||
allQueryTypes.forEach(query => {
|
||||
permNoCheck(tableName, query);
|
||||
});
|
||||
} else {
|
||||
allQueryTypes.forEach(query => {
|
||||
permCustomCheck(tableName, query);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const trackView = () => {
|
||||
// track view data-tab-link
|
||||
cy.get(getElementFromAlias('data-tab-link')).click();
|
||||
cy.wait(7000);
|
||||
cy.get(
|
||||
getElementFromAlias(`add-track-table-${getTableName(1, testName)}`)
|
||||
).click();
|
||||
cy.wait(10000);
|
||||
// Move to permissions
|
||||
cy.get(getElementFromAlias('table-permissions')).click();
|
||||
};
|
||||
|
||||
export const createView = (viewName: string, tableName: string) => {
|
||||
const reqBody = {
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
sql: `create view "${viewName}" as select * from "${tableName}"`,
|
||||
},
|
||||
};
|
||||
cy.window().then(win => {
|
||||
const { __env } = win;
|
||||
const requestOptions = makeDataAPIOptions(
|
||||
__env.dataApiUrl,
|
||||
__env.adminSecret,
|
||||
reqBody
|
||||
);
|
||||
cy.request(requestOptions);
|
||||
});
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
import { baseUrl, getElementFromAlias } from '../../../helpers/dataHelpers';
|
||||
|
||||
let prevStr = '';
|
||||
|
||||
export const openRawSQL = () => {
|
||||
// Open RawSQL
|
||||
cy.get('a').contains('Data').click();
|
||||
cy.wait(3000);
|
||||
cy.get(getElementFromAlias('sql-link')).click();
|
||||
cy.wait(3000);
|
||||
// Match URL
|
||||
cy.url().should('eq', `${baseUrl}/data/sql`);
|
||||
};
|
||||
const clearText = () => {
|
||||
cy.get('textarea').type('{selectall}', { force: true });
|
||||
cy.get('textarea').trigger('keydown', {
|
||||
keyCode: 46,
|
||||
which: 46,
|
||||
force: true,
|
||||
});
|
||||
cy.wait(2000); // ace editor textarea doesn't expose the value to check, so wait
|
||||
};
|
||||
|
||||
export const passCreateTable = () => {
|
||||
prevStr = 'CREATE TABLE Apic_test_table_rsql (id serial PRIMARY KEY);';
|
||||
cy.get('textarea').type(prevStr, { force: true });
|
||||
cy.wait(1000); // debounce
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
// cy.get(getElementFromAlias('raw-sql-statement-timeout'));
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const passInsertValues = () => {
|
||||
clearText();
|
||||
// eslint-disable-next-line prefer-spread
|
||||
const str = Array.apply(null, Array(15))
|
||||
.map(
|
||||
(_, ix: number) => `INSERT INTO Apic_test_table_rsql VALUES (${+ix + 1});`
|
||||
)
|
||||
.join('\n');
|
||||
cy.get('textarea').type(str, { force: true });
|
||||
cy.wait(1000);
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const readQuery = () => {
|
||||
clearText();
|
||||
prevStr = 'SELECT * FROM public.Apic_test_table_rsql;';
|
||||
cy.get('textarea').type(prevStr, { force: true });
|
||||
cy.wait(1000); // debounce
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
cy.wait(3000); // debounce
|
||||
cy.get('div.rt-tr-group').should('have.length', 10);
|
||||
cy.get('button.-btn').last().click();
|
||||
cy.wait(500);
|
||||
cy.get('div.rt-tr-group').should('have.length', 5);
|
||||
cy.get('div.rt-td').first().should('have.text', '11');
|
||||
cy.get('div.rt-td').last().should('have.text', 'NULL');
|
||||
};
|
||||
export const passAlterTable = () => {
|
||||
clearText();
|
||||
prevStr = 'ALTER TABLE Apic_test_table_rsql ADD COLUMN name text;';
|
||||
cy.get('textarea').type(prevStr, { force: true });
|
||||
// Untrack table
|
||||
cy.wait(1000);
|
||||
cy.get(getElementFromAlias('raw-sql-track-check')).uncheck();
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const passCreateView = () => {
|
||||
clearText();
|
||||
prevStr = 'CREATE VIEW abcd AS SELECT * FROM Apic_test_table_rsql;';
|
||||
cy.get('textarea').type(prevStr, { force: true });
|
||||
// Track table
|
||||
cy.wait(1000);
|
||||
cy.get(getElementFromAlias('raw-sql-track-check')).check();
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const delTestTables = () => {
|
||||
clearText();
|
||||
prevStr = 'DROP TABLE Apic_test_table_rsql CASCADE;';
|
||||
cy.get('textarea').type(prevStr, { force: true });
|
||||
cy.wait(1000);
|
||||
cy.get(getElementFromAlias('raw-sql-migration-check')).uncheck();
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
// NOTE: This is only visible, when the console is in CLI mode
|
||||
cy.get(getElementFromAlias('not-migration-confirm')).click();
|
||||
// cy.get(getElementFromAlias('raw-sql-statement-timeout')).type('20', {
|
||||
// force: true,
|
||||
// });
|
||||
cy.wait(5000);
|
||||
};
|
@ -1,40 +0,0 @@
|
||||
import {
|
||||
openRawSQL,
|
||||
passCreateTable,
|
||||
delTestTables,
|
||||
passCreateView,
|
||||
passInsertValues,
|
||||
passAlterTable,
|
||||
readQuery,
|
||||
} from './spec';
|
||||
import { testMode } from '../../../helpers/common';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
describe('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
// Visit the index route
|
||||
cy.visit(getIndexRoute());
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runRawSQLTests = () => {
|
||||
describe('Raw SQL', () => {
|
||||
it('Open Raw SQL page', openRawSQL);
|
||||
it('Pass create table', passCreateTable);
|
||||
it('Pass insert values', passInsertValues);
|
||||
it('Pass alter table', passAlterTable);
|
||||
it('Read from table', readQuery);
|
||||
it('Pass create view', passCreateView);
|
||||
it('Delete test table', delTestTables);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runRawSQLTests();
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,326 +0,0 @@
|
||||
import {
|
||||
baseUrl,
|
||||
getElementFromAlias,
|
||||
tableColumnTypeSelector,
|
||||
getIndexRoute,
|
||||
} from '../../../helpers/dataHelpers';
|
||||
|
||||
import {
|
||||
setMetaData,
|
||||
validateCT,
|
||||
validateColumn,
|
||||
ResultType,
|
||||
TableFields,
|
||||
} from '../../validators/validators';
|
||||
import { setPromptValue } from '../../../helpers/common';
|
||||
|
||||
const AWAIT_SHORT = 2000;
|
||||
|
||||
const delRel = (table: string, relname: string) => {
|
||||
cy.get(getElementFromAlias(table)).click();
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.get(getElementFromAlias(`relationship-toggle-editor-${relname}`)).click();
|
||||
cy.get(getElementFromAlias(`relationship-remove-${relname}`)).click();
|
||||
cy.on('window:alert', str => {
|
||||
expect(str === 'Are you sure?').to.be.true;
|
||||
});
|
||||
cy.wait(15000);
|
||||
};
|
||||
|
||||
export const createTable = (name: string, fields: TableFields) => {
|
||||
// Click on the "Add table" button and input the table name
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public/table/add`);
|
||||
cy.get(getElementFromAlias('tableName')).type(`${name}_table_rt`);
|
||||
|
||||
// Enter column info
|
||||
let i = 0;
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const key in fields) {
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (fields.hasOwnProperty(key)) {
|
||||
cy.get(getElementFromAlias(`column-${i}`)).type(key);
|
||||
tableColumnTypeSelector(`col-type-${i}`);
|
||||
cy.get(getElementFromAlias(`data_test_column_type_value_${fields[key]}`))
|
||||
.first()
|
||||
.click();
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Select primary key
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('id');
|
||||
|
||||
if (name === 'article') {
|
||||
cy.get(getElementFromAlias('add-table-edit-fk-0')).click();
|
||||
cy.get(getElementFromAlias('foreign-key-ref-table-0')).select(
|
||||
'author_table_rt'
|
||||
);
|
||||
cy.get(getElementFromAlias('foreign-key-0-lcol-0')).select('3');
|
||||
cy.get(getElementFromAlias('foreign-key-0-rcol-0')).select('id');
|
||||
cy.get(getElementFromAlias('foreign-key-0-onUpdate-cascade')).check();
|
||||
cy.get(getElementFromAlias('foreign-key-0-onDelete-cascade')).check();
|
||||
} else if (name === 'comment') {
|
||||
cy.get(getElementFromAlias('add-table-edit-fk-0')).click();
|
||||
cy.get(getElementFromAlias('foreign-key-ref-table-0')).select(
|
||||
'author_table_rt'
|
||||
);
|
||||
cy.get(getElementFromAlias('foreign-key-0-lcol-0')).select('1');
|
||||
cy.get(getElementFromAlias('foreign-key-0-rcol-0')).select('id');
|
||||
cy.get(getElementFromAlias('foreign-key-0-onUpdate-cascade')).check();
|
||||
cy.get(getElementFromAlias('foreign-key-0-onDelete-cascade')).check();
|
||||
cy.get(getElementFromAlias('add-table-edit-fk-1')).click();
|
||||
cy.get(getElementFromAlias('foreign-key-ref-table-1')).select(
|
||||
'article_table_rt'
|
||||
);
|
||||
cy.get(getElementFromAlias('foreign-key-1-lcol-0')).select('2');
|
||||
cy.get(getElementFromAlias('foreign-key-1-rcol-0')).select('id');
|
||||
cy.get(getElementFromAlias('foreign-key-1-onUpdate-cascade')).check();
|
||||
cy.get(getElementFromAlias('foreign-key-1-onDelete-cascade')).check();
|
||||
}
|
||||
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(15000);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${name}_table_rt/modify`
|
||||
);
|
||||
|
||||
validateCT(`${name}_table_rt`, ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const passRTCreateTables = () => {
|
||||
createTable('author', { id: 'integer', name: 'text' });
|
||||
createTable('article', {
|
||||
id: 'integer',
|
||||
title: 'text',
|
||||
Content: 'text',
|
||||
author_id: 'integer',
|
||||
rating: 'integer',
|
||||
});
|
||||
createTable('comment', {
|
||||
id: 'integer',
|
||||
user_id: 'integer',
|
||||
article_id: 'integer',
|
||||
comment: 'text',
|
||||
});
|
||||
};
|
||||
|
||||
export const Deletetable = (name: string) => {
|
||||
cy.get(getElementFromAlias(name)).click();
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
setPromptValue(name);
|
||||
cy.get(getElementFromAlias('delete-table')).click();
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(15000);
|
||||
validateCT(name, ResultType.FAILURE);
|
||||
};
|
||||
|
||||
export const passRTDeleteTables = () => {
|
||||
Deletetable('comment_table_rt');
|
||||
Deletetable('article_table_rt');
|
||||
Deletetable('author_table_rt');
|
||||
};
|
||||
|
||||
export const passRTAddManualObjRel = () => {
|
||||
cy.get(getElementFromAlias('article_table_rt')).click();
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.wait(4000);
|
||||
cy.get(getElementFromAlias('article_table_rt')).click();
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.get(getElementFromAlias('create-edit-manual-rel')).click();
|
||||
cy.get(getElementFromAlias('manual-relationship-type')).select('object');
|
||||
cy.get("input[placeholder='Enter relationship name']").type('author');
|
||||
cy.get(getElementFromAlias('manual-relationship-ref-schema')).select(
|
||||
'public'
|
||||
);
|
||||
cy.get(getElementFromAlias('manual-relationship-ref-table')).select(
|
||||
'author_table_rt'
|
||||
);
|
||||
cy.get(getElementFromAlias('manual-relationship-lcol-0')).select('author_id');
|
||||
cy.get(getElementFromAlias('manual-relationship-rcol-0')).select('id');
|
||||
cy.get(getElementFromAlias('create-manual-rel-save')).click();
|
||||
cy.wait(15000);
|
||||
validateColumn(
|
||||
'article_table_rt',
|
||||
['title', { name: 'author', columns: ['name'] }],
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
};
|
||||
|
||||
export const passRTAddManualArrayRel = () => {
|
||||
cy.get(getElementFromAlias('article_table_rt')).click();
|
||||
cy.wait(4000);
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.get(getElementFromAlias('create-edit-manual-rel')).click();
|
||||
cy.get(getElementFromAlias('manual-relationship-type')).select('array');
|
||||
cy.get("input[placeholder='Enter relationship name']").type('comments');
|
||||
cy.get(getElementFromAlias('manual-relationship-ref-schema')).select(
|
||||
'public'
|
||||
);
|
||||
cy.get(getElementFromAlias('manual-relationship-ref-table')).select(
|
||||
'comment_table_rt'
|
||||
);
|
||||
cy.get(getElementFromAlias('manual-relationship-lcol-0')).select('id');
|
||||
cy.get(getElementFromAlias('manual-relationship-rcol-0')).select(
|
||||
'article_id'
|
||||
);
|
||||
cy.get(getElementFromAlias('create-manual-rel-save')).click();
|
||||
cy.wait(15000);
|
||||
validateColumn(
|
||||
'article_table_rt',
|
||||
['title', { name: 'comments', columns: ['comment'] }],
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
};
|
||||
|
||||
export const passRTDeleteRelationships = () => {
|
||||
delRel('article_table_rt', 'author');
|
||||
validateColumn(
|
||||
'article_table_rt',
|
||||
['title', { name: 'author', columns: ['name'] }],
|
||||
ResultType.FAILURE
|
||||
);
|
||||
delRel('article_table_rt', 'comments');
|
||||
validateColumn(
|
||||
'article_table_rt',
|
||||
['title', { name: 'comments', columns: ['comment'] }],
|
||||
ResultType.FAILURE
|
||||
);
|
||||
};
|
||||
|
||||
export const passRTAddSuggestedRel = () => {
|
||||
cy.get(getElementFromAlias('article_table_rt')).click();
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.get(getElementFromAlias('obj-rel-add-0')).click();
|
||||
cy.get(getElementFromAlias('suggested-rel-name')).clear().type('author');
|
||||
cy.get(getElementFromAlias('obj-rel-save-0')).click();
|
||||
cy.wait(5000);
|
||||
validateColumn(
|
||||
'article_table_rt',
|
||||
['title', { name: 'author', columns: ['name'] }],
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
cy.get(getElementFromAlias('article_table_rt')).click();
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.get(getElementFromAlias('arr-rel-add-0')).click();
|
||||
cy.get(getElementFromAlias('suggested-rel-name')).clear().type('comments');
|
||||
cy.get(getElementFromAlias('arr-rel-save-0')).click();
|
||||
cy.wait(5000);
|
||||
validateColumn(
|
||||
'article_table_rt',
|
||||
['title', { name: 'comments', columns: ['comment'] }],
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
};
|
||||
|
||||
export const passRTRenameRelationship = () => {
|
||||
cy.get(getElementFromAlias('relationship-toggle-editor-comments')).click();
|
||||
cy.get(getElementFromAlias('relationship-name-input-comments'))
|
||||
.clear()
|
||||
.type('comments_renamed');
|
||||
cy.get(getElementFromAlias('relationship-save-comments')).click();
|
||||
cy.wait(5000);
|
||||
validateColumn(
|
||||
'article_table_rt',
|
||||
['title', { name: 'comments_renamed', columns: ['comment'] }],
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
cy.get(
|
||||
getElementFromAlias('relationship-toggle-editor-comments_renamed')
|
||||
).click();
|
||||
cy.get(getElementFromAlias('relationship-name-input-comments_renamed'))
|
||||
.clear()
|
||||
.type('comments');
|
||||
cy.get(getElementFromAlias('relationship-save-comments_renamed')).click();
|
||||
cy.wait(5000);
|
||||
validateColumn(
|
||||
'article_table_rt',
|
||||
['title', { name: 'comments', columns: ['comment'] }],
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
};
|
||||
|
||||
export const failRTAddSuggestedRel = () => {
|
||||
cy.get(getElementFromAlias('article_table_rt')).click();
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.get(getElementFromAlias('obj-rel-add-0')).click();
|
||||
cy.get(getElementFromAlias('suggested-rel-name')).clear();
|
||||
cy.get(getElementFromAlias('obj-rel-save-0')).click();
|
||||
cy.wait(15000);
|
||||
cy.get(getElementFromAlias('suggested-rel-name')).clear().type(`${123123}`);
|
||||
cy.get('button').contains('Save').click();
|
||||
cy.wait(15000);
|
||||
validateColumn(
|
||||
'article_table_rt',
|
||||
['title', { name: 'author', columns: ['name'] }],
|
||||
ResultType.FAILURE
|
||||
);
|
||||
cy.get(getElementFromAlias('article_table_rt')).click();
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.get(getElementFromAlias('obj-rel-add-0')).click();
|
||||
cy.get(getElementFromAlias('suggested-rel-name')).clear().type('author');
|
||||
cy.get(getElementFromAlias('obj-rel-save-0')).click();
|
||||
cy.wait(15000);
|
||||
validateColumn(
|
||||
'article_table_rt',
|
||||
['title', { name: 'author', columns: ['name'] }],
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
cy.get(getElementFromAlias('article_table_rt')).click();
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.get(getElementFromAlias('arr-rel-add-0')).click();
|
||||
cy.get(getElementFromAlias('suggested-rel-name')).clear().type('comments');
|
||||
cy.get(getElementFromAlias('arr-rel-save-0')).click();
|
||||
cy.wait(15000);
|
||||
delRel('article_table_rt', 'author');
|
||||
delRel('article_table_rt', 'comments');
|
||||
};
|
||||
|
||||
export const passRSTSetup = () => {
|
||||
createTable('countries', {
|
||||
id: 'integer',
|
||||
name: 'text',
|
||||
countryCode: 'text',
|
||||
});
|
||||
};
|
||||
|
||||
export const passRSTReset = () => {
|
||||
Deletetable('countries_table_rt');
|
||||
};
|
||||
|
||||
const AWAIT_MINOR = 500;
|
||||
|
||||
export const passRSTAddRSRel = () => {
|
||||
cy.reload();
|
||||
cy.getBySel('countries_table_rt').click();
|
||||
cy.getBySel('table-relationships').click();
|
||||
cy.getBySel('table-relationship-edit-remote-relationship-add').click();
|
||||
cy.wait(AWAIT_MINOR);
|
||||
cy.getBySel('remote-rel-name-input').type('remote');
|
||||
cy.getBySel('remote-rel-schema-input').select('remote_rel_test_rs');
|
||||
cy.get('input[type="checkbox"]').eq(3).check();
|
||||
cy.wait(AWAIT_MINOR);
|
||||
cy.get('input[type="checkbox"]').eq(4).check();
|
||||
cy.get('select').eq(2).select('countryCode');
|
||||
cy.getBySel('table-relationship-remote-relationship-add-save').click();
|
||||
cy.get('.notification', { timeout: AWAIT_SHORT })
|
||||
.should('be.visible')
|
||||
.and('contain', 'Successfully created remote relationship');
|
||||
};
|
||||
|
||||
export const passRSTDeleteRSRel = () => {
|
||||
cy.reload();
|
||||
cy.getBySel('table-relationship-edit-remote-relationship-edit').click();
|
||||
cy.getBySel('table-relationship-remote-relationship-edit-remove').click();
|
||||
cy.get('.notification', { timeout: AWAIT_SHORT })
|
||||
.should('be.visible')
|
||||
.and('contain', 'Successfully deleted remote relationship');
|
||||
};
|
||||
|
||||
export const setValidationMetaData = () => {
|
||||
setMetaData();
|
||||
};
|
@ -1,46 +0,0 @@
|
||||
import {
|
||||
passRTCreateTables,
|
||||
passRTDeleteTables,
|
||||
passRTAddManualObjRel,
|
||||
passRTAddManualArrayRel,
|
||||
passRTDeleteRelationships,
|
||||
passRTAddSuggestedRel,
|
||||
failRTAddSuggestedRel,
|
||||
passRTRenameRelationship,
|
||||
} from './spec';
|
||||
import { testMode } from '../../../helpers/common';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Check Data Tab', () => {
|
||||
it('Clicking on Data tab opens the correct route', () => {
|
||||
cy.visit(getIndexRoute());
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runRelationshipsTests = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Relationships Tests', () => {
|
||||
it('Create testing tables', passRTCreateTables);
|
||||
it('Add Manual Relationship Object', passRTAddManualObjRel);
|
||||
it('Add Manual Relationship Array', passRTAddManualArrayRel);
|
||||
it('Delete the relationships', passRTDeleteRelationships);
|
||||
it('Add Suggested Relationships Error', failRTAddSuggestedRel);
|
||||
it('Add Suggested Relationships', passRTAddSuggestedRel);
|
||||
it('Rename relationships', passRTRenameRelationship);
|
||||
it('Delete the relationships', passRTDeleteRelationships);
|
||||
it('Delete test tables', passRTDeleteTables);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runRelationshipsTests();
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
import { getDbRoute } from '../../../helpers/dataHelpers';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { setPromptValue, testMode } from '../../../helpers/common';
|
||||
|
||||
const setup = () => {
|
||||
describe('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
cy.visit(getDbRoute());
|
||||
cy.wait(7000);
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runSchemaSharingTests = () => {
|
||||
describe('template gallery', () => {
|
||||
it('display content', () => {
|
||||
cy.get('[data-test=table-links]').contains('default').click();
|
||||
cy.get('table').contains('Relationships: One-to-One').click();
|
||||
cy.contains('Install Template').click();
|
||||
cy.wait(1000);
|
||||
cy.get('[data-test=table-links]').contains('_onetoone').click();
|
||||
setPromptValue('_onetoone');
|
||||
cy.contains('_onetoone').parent().parent().contains('owner');
|
||||
cy.contains('_onetoone').parent().parent().contains('passport_info');
|
||||
cy.get('[title="Delete current schema"]').click();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runSchemaSharingTests();
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,415 +0,0 @@
|
||||
import {
|
||||
getElementFromAlias,
|
||||
baseUrl,
|
||||
tableColumnTypeSelector,
|
||||
getIndexRoute,
|
||||
} from '../../../helpers/dataHelpers';
|
||||
|
||||
import {
|
||||
setMetaData,
|
||||
validateCT,
|
||||
createView,
|
||||
validateColumn,
|
||||
validateView,
|
||||
ResultType,
|
||||
TableFields,
|
||||
} from '../../validators/validators';
|
||||
import { setPromptValue } from '../../../helpers/common';
|
||||
|
||||
const userId = 5555;
|
||||
|
||||
export const createTable = (name: string, dict: TableFields) => {
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public/table/add`);
|
||||
cy.get(getElementFromAlias('tableName')).type(`${name}_table_vt`);
|
||||
const keys = Object.keys(dict).map(k => k);
|
||||
const values = Object.keys(dict).map(k => dict[k]);
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
cy.get(getElementFromAlias(`column-${i}`)).type(keys[i]);
|
||||
tableColumnTypeSelector(`col-type-${i}`);
|
||||
cy.get(getElementFromAlias(`data_test_column_type_value_${values[i]}`))
|
||||
.first()
|
||||
.click();
|
||||
}
|
||||
cy.get(getElementFromAlias('primary-key-select-0')).select('id');
|
||||
cy.get(getElementFromAlias('table-create')).click();
|
||||
cy.wait(7000);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${name}_table_vt/modify`
|
||||
);
|
||||
|
||||
validateCT(`${name}_table_vt`, ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const passVCreateTables = () => {
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
createTable('author', { id: 'integer', name: 'text' });
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
createTable('article', {
|
||||
id: 'integer',
|
||||
title: 'text',
|
||||
Content: 'text',
|
||||
author_id: 'integer',
|
||||
rating: 'integer',
|
||||
});
|
||||
cy.visit(getIndexRoute());
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
createTable('comment', {
|
||||
id: 'integer',
|
||||
user_id: 'integer',
|
||||
article_id: 'integer',
|
||||
comment: 'text',
|
||||
});
|
||||
};
|
||||
|
||||
export const passVCreateViews = () => {
|
||||
createView(`CREATE VIEW author_average_rating_vt AS
|
||||
SELECT author_table_vt.id, avg(article_table_vt.rating)
|
||||
From author_table_vt, article_table_vt
|
||||
WHERE author_table_vt.id = article_table_vt.author_id
|
||||
GROUP BY author_table_vt.id`);
|
||||
};
|
||||
|
||||
export const passTrackTable = () => {
|
||||
cy.visit('/data/default/schema/public/');
|
||||
cy.wait(7000);
|
||||
cy.get(
|
||||
getElementFromAlias('add-track-table-author_average_rating_vt')
|
||||
).click();
|
||||
cy.wait(7000);
|
||||
validateView('author_average_rating_vt', ResultType.SUCCESS);
|
||||
};
|
||||
|
||||
export const passViewRoute = () => {
|
||||
cy.get(getElementFromAlias('author_average_rating_vt')).click();
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/views/author_average_rating_vt/browse`
|
||||
);
|
||||
};
|
||||
|
||||
type Data = (string | number)[];
|
||||
export const passVAddDataarticle = (data: Data, index: number) => {
|
||||
// Click the Insert Again button.
|
||||
cy.get('label')
|
||||
.contains('id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label').contains('id').next().find('input').last().type(`${data[0]}`);
|
||||
cy.get('label')
|
||||
.contains('title')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('title')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[1]}`);
|
||||
cy.get('label')
|
||||
.contains('Content')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('Content')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[2]}`);
|
||||
cy.get('label')
|
||||
.contains('author_id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('author_id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[3]}`);
|
||||
cy.get('label')
|
||||
.contains('rating')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('rating')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[4]}`);
|
||||
if (index) {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
} else {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
}
|
||||
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const passVAddDataauthor = (data: Data, index: number) => {
|
||||
cy.get('label')
|
||||
.contains('id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label').contains('id').next().find('input').last().type(`${data[0]}`);
|
||||
cy.get('label')
|
||||
.contains('name')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('name')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[1]}`);
|
||||
if (index) {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
} else {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
}
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const passVAddDatacomment = (data: Data, index: number) => {
|
||||
cy.get('label')
|
||||
.contains('id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label').contains('id').next().find('input').last().type(`${data[0]}`);
|
||||
cy.get('label')
|
||||
.contains('user_id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('user_id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[1]}`);
|
||||
cy.get('label')
|
||||
.contains('article_id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('article_id')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[2]}`);
|
||||
cy.get('label')
|
||||
.contains('comment')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type('{selectall}{del}');
|
||||
cy.get('label')
|
||||
.contains('comment')
|
||||
.next()
|
||||
.find('input')
|
||||
.last()
|
||||
.type(`${data[3]}`);
|
||||
if (index) {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
} else {
|
||||
cy.get(getElementFromAlias('insert-save-button')).click();
|
||||
}
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
const checkQuerySuccess = () => {
|
||||
// Expect only 4 rows i.e. expect fifth element to not exist
|
||||
cy.get('[role=gridcell]').contains(userId);
|
||||
cy.get('[role=row]').eq(2).should('not.exist');
|
||||
};
|
||||
|
||||
export const passVAddData = () => {
|
||||
let data;
|
||||
cy.get(getElementFromAlias('article_table_vt')).click();
|
||||
cy.get(getElementFromAlias('table-insert-rows')).click();
|
||||
data = [1, 'A', 'Sontent', userId, 4];
|
||||
passVAddDataarticle(data, 0);
|
||||
data = [2, 'B', 'Sontenta', 2, 4];
|
||||
passVAddDataarticle(data, 1);
|
||||
data = [3, 'C', 'Sontentb', userId, 4];
|
||||
passVAddDataarticle(data, 2);
|
||||
cy.get(getElementFromAlias('author_table_vt')).click();
|
||||
cy.get(getElementFromAlias('table-insert-rows')).click();
|
||||
|
||||
data = [userId, 'A'];
|
||||
passVAddDataauthor(data, 0);
|
||||
data = [2, 'B'];
|
||||
passVAddDataauthor(data, 1);
|
||||
cy.get(getElementFromAlias('comment_table_vt')).click();
|
||||
cy.get(getElementFromAlias('table-insert-rows')).click();
|
||||
|
||||
data = [1, 1, 1, 'new comment'];
|
||||
passVAddDatacomment(data, 0);
|
||||
data = [2, 2, 2, 'new comment'];
|
||||
passVAddDatacomment(data, 1);
|
||||
data = [3, 1, 2, 'new comment'];
|
||||
passVAddDatacomment(data, 2);
|
||||
};
|
||||
|
||||
export const passVFilterQueryEq = () => {
|
||||
// Select column with type `text`
|
||||
cy.get('select')
|
||||
.find('option')
|
||||
.contains('-- column --')
|
||||
.parent()
|
||||
.first()
|
||||
.select('id');
|
||||
// Type value as `filter-text`
|
||||
cy.get("input[placeholder='-- value --']").last().type(`${userId}`);
|
||||
// Run query
|
||||
cy.get(getElementFromAlias('run-query')).click();
|
||||
cy.wait(5000);
|
||||
// Check if the query was successful
|
||||
checkQuerySuccess();
|
||||
};
|
||||
|
||||
const checkOrder = (order: string) => {
|
||||
// Utility function to get right element
|
||||
if (order === 'asc') {
|
||||
cy.get('[role=row]').each(($el, index) => {
|
||||
if (index === 1) {
|
||||
cy.wrap($el).find('[role=gridcell]').first().next().next().contains(2);
|
||||
}
|
||||
if (index === 2) {
|
||||
cy.wrap($el)
|
||||
.find('[role=gridcell]')
|
||||
.first()
|
||||
.next()
|
||||
.next()
|
||||
.contains(userId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cy.get('[role=row]').each(($el, index) => {
|
||||
if (index === 2) {
|
||||
cy.wrap($el).find('[role=gridcell]').first().next().next().contains(2);
|
||||
}
|
||||
if (index === 1) {
|
||||
cy.wrap($el)
|
||||
.find('[role=gridcell]')
|
||||
.first()
|
||||
.next()
|
||||
.next()
|
||||
.contains(userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const passVAscendingSort = () => {
|
||||
cy.wait(7000);
|
||||
// Select column with type 'serial'
|
||||
cy.get('select')
|
||||
.find('option')
|
||||
.contains('-- column --')
|
||||
.parent()
|
||||
.last()
|
||||
.select('id');
|
||||
// Run query
|
||||
cy.get(getElementFromAlias('run-query')).click();
|
||||
// Check order
|
||||
checkOrder('asc');
|
||||
};
|
||||
|
||||
export const passModifyView = () => {
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
cy.get('button').contains('Modify').last().click();
|
||||
cy.url().should('eq', `${baseUrl}/data/sql`);
|
||||
};
|
||||
|
||||
export const passVAddManualObjRel = () => {
|
||||
cy.get(getElementFromAlias('author_average_rating_vt')).click();
|
||||
cy.wait(2000);
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.wait(2000);
|
||||
cy.get(getElementFromAlias('create-edit-manual-rel')).click();
|
||||
cy.get(getElementFromAlias('manual-relationship-type')).select('object');
|
||||
cy.get("input[placeholder='Enter relationship name']").type('author');
|
||||
cy.get(getElementFromAlias('manual-relationship-ref-schema')).select(
|
||||
'public'
|
||||
);
|
||||
cy.get(getElementFromAlias('manual-relationship-ref-table')).select(
|
||||
'author_table_vt'
|
||||
);
|
||||
cy.get(getElementFromAlias('manual-relationship-lcol-0')).select('id');
|
||||
cy.get(getElementFromAlias('manual-relationship-rcol-0')).select('id');
|
||||
cy.get(getElementFromAlias('create-manual-rel-save')).click();
|
||||
cy.wait(7000);
|
||||
validateColumn(
|
||||
'author_average_rating_vt',
|
||||
['avg', { name: 'author', columns: ['name'] }],
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
};
|
||||
|
||||
export const passVDeleteRelationships = () => {
|
||||
cy.get(getElementFromAlias('author_average_rating_vt')).click();
|
||||
cy.get(getElementFromAlias('table-relationships')).click();
|
||||
cy.get(getElementFromAlias('relationship-toggle-editor-author')).click();
|
||||
cy.get(getElementFromAlias('relationship-remove-author')).click();
|
||||
cy.on('window:alert', str => {
|
||||
return expect(str === 'Are you sure?').to.be.true;
|
||||
});
|
||||
cy.wait(7000);
|
||||
validateColumn(
|
||||
'author_average_rating_vt',
|
||||
['avg', { name: 'author', columns: ['name'] }],
|
||||
ResultType.FAILURE
|
||||
);
|
||||
};
|
||||
|
||||
export const passVDeleteView = () => {
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
setPromptValue('author_average_rating_vt');
|
||||
cy.get(getElementFromAlias('delete-view')).click();
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(7000);
|
||||
validateView('author_average_rating_vt', ResultType.FAILURE);
|
||||
};
|
||||
|
||||
export const deleteTable = (name: string) => {
|
||||
cy.get(getElementFromAlias(name)).click();
|
||||
cy.get(getElementFromAlias('table-modify')).click();
|
||||
setPromptValue(name);
|
||||
cy.get(getElementFromAlias('delete-table')).click();
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(7000);
|
||||
validateCT(name, ResultType.FAILURE);
|
||||
cy.wait(7000);
|
||||
};
|
||||
|
||||
export const passVDeleteTables = () => {
|
||||
deleteTable('comment_table_vt');
|
||||
deleteTable('article_table_vt');
|
||||
deleteTable('author_table_vt');
|
||||
};
|
||||
|
||||
export const setValidationMetaData = () => {
|
||||
setMetaData();
|
||||
};
|
@ -1,57 +0,0 @@
|
||||
import {
|
||||
passVCreateTables,
|
||||
passVCreateViews,
|
||||
passVAddData,
|
||||
passTrackTable,
|
||||
passVAddManualObjRel,
|
||||
passVAscendingSort,
|
||||
passVFilterQueryEq,
|
||||
passViewRoute,
|
||||
passModifyView,
|
||||
passVDeleteRelationships,
|
||||
passVDeleteView,
|
||||
passVDeleteTables,
|
||||
} from './spec';
|
||||
import { testMode } from '../../../helpers/common';
|
||||
import { setMetaData } from '../../validators/validators';
|
||||
import { getIndexRoute } from '../../../helpers/dataHelpers';
|
||||
|
||||
const setup = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Setup route', () => {
|
||||
it('Visit the index route', () => {
|
||||
// Visit the index route
|
||||
cy.visit(getIndexRoute());
|
||||
// Get and set validation metadata
|
||||
setMetaData();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const runViewsTest = () => {
|
||||
// Temporarily skipped because of its flakiness, see: https://github.com/hasura/graphql-engine-mono/issues/5433
|
||||
// TODO: Fix and restore it
|
||||
describe.skip('Views', () => {
|
||||
// NOTE: Ideally, we should be adding "should" at the beginning of
|
||||
// the test descriptions. It will sound like this when you read it.
|
||||
// eg. it should create test tables ...and so on
|
||||
it('Create Tables', passVCreateTables);
|
||||
it('Insert test data to table(s)', passVAddData);
|
||||
it('Create View', passVCreateViews);
|
||||
it('Add View to comment table', passTrackTable);
|
||||
it('Visit the view route', passViewRoute);
|
||||
it('Order Ascending order View Table', passVAscendingSort);
|
||||
it('Apply Filters on the View', passVFilterQueryEq);
|
||||
it('Modify the View', passModifyView);
|
||||
it('Add Object Relationship to View', passVAddManualObjRel);
|
||||
it('Delete Relationship(s)', passVDeleteRelationships);
|
||||
it('Delete View', passVDeleteView);
|
||||
it('Delete Tables', passVDeleteTables);
|
||||
});
|
||||
};
|
||||
|
||||
if (testMode !== 'cli') {
|
||||
setup();
|
||||
runViewsTest();
|
||||
}
|
@ -1,315 +0,0 @@
|
||||
/**
|
||||
* This e2e test is intended to be run manually to verify if a new database is compatible — not to run on the CI
|
||||
*
|
||||
* NOTE: You need a local cypress.env.json file with some Env Variables set to make it running (connection strings).
|
||||
*
|
||||
* Example of cypress.env.json content:
|
||||
* {
|
||||
* "POSTGRES_DB_CONNECTION_STRING": "postgres://postgres:postgrespassword@postgres:5432/postgres",
|
||||
* }
|
||||
*/
|
||||
|
||||
import { Driver } from '../../../src/dataSources';
|
||||
|
||||
// NOTE: table name cannot start with number
|
||||
const getRandomString = () => `a${Math.random().toString(36).slice(3)}`;
|
||||
|
||||
const connectionString = Cypress.env('POSTGRES_DB_CONNECTION_STRING');
|
||||
const driverSelectLabel = 'Postgres';
|
||||
const driverSelectValue: Driver = 'postgres';
|
||||
const dataSourceDisplayName = getRandomString();
|
||||
const tableName = getRandomString();
|
||||
const consolePort = '3000';
|
||||
|
||||
describe.skip('Test Basic Data Source Compatibility', () => {
|
||||
it(
|
||||
'tests Connect Data Source',
|
||||
{
|
||||
defaultCommandTimeout: 20000,
|
||||
},
|
||||
() => {
|
||||
cy.visit(`http://localhost:${consolePort}/data/manage/connect`);
|
||||
|
||||
cy.log('/** Type the data source name **/');
|
||||
cy.get('[data-test=database-display-name]').click();
|
||||
|
||||
cy.get('[data-test=database-display-name]').type(dataSourceDisplayName);
|
||||
|
||||
cy.log('/** Select the Driver **/');
|
||||
|
||||
cy.get('[data-test=database-type]')
|
||||
.select(driverSelectLabel, { force: true })
|
||||
.should('have.value', driverSelectValue);
|
||||
|
||||
cy.log('/** Type the connection string **/');
|
||||
cy.get('[data-test=database-url]').click();
|
||||
|
||||
cy.get('[data-test=database-url]').type(connectionString);
|
||||
|
||||
cy.log('/** Click connect **/');
|
||||
cy.get('[data-test=connect-database-btn] > span').click();
|
||||
|
||||
cy.log('/** Expect to see a success notification **/');
|
||||
cy.wait(5000);
|
||||
cy.get('.notification-success.notification-visible').should(
|
||||
'contain',
|
||||
'Data source added successfully!'
|
||||
);
|
||||
|
||||
cy.get('.notification-success').should('be.visible');
|
||||
}
|
||||
);
|
||||
|
||||
it(
|
||||
'tests create table',
|
||||
{
|
||||
defaultCommandTimeout: 20000,
|
||||
},
|
||||
() => {
|
||||
cy.visit(`http://localhost:${consolePort}/data/manage`);
|
||||
|
||||
cy.log('/** Click on the database in the Nav Tree **/');
|
||||
cy.contains('Data Manager')
|
||||
.parent('li')
|
||||
.within(() => {
|
||||
cy.findByText(dataSourceDisplayName).click();
|
||||
});
|
||||
|
||||
cy.log('/** Click on schema in the Nav Tree **/');
|
||||
cy.get('[data-test=table-links]').within(() => {
|
||||
cy.findByText('public').click({ force: true });
|
||||
});
|
||||
|
||||
cy.log('/** Click on create table **/');
|
||||
cy.get('[data-test=data-create-table]').click();
|
||||
|
||||
cy.log('/** Type the table name **/');
|
||||
cy.get('[data-test=tableName]').type(tableName);
|
||||
|
||||
cy.log('/** Click on the table comment input **/');
|
||||
cy.get('[data-test=tableComment]').click();
|
||||
|
||||
cy.log('/** Type the table comment **/');
|
||||
cy.get('[data-test=tableComment]').type('My table comment');
|
||||
|
||||
cy.log('/** Click on column name **/');
|
||||
cy.get('[data-test=column-0]').click();
|
||||
|
||||
cy.log('/** Click on "Frequently used columns" **/');
|
||||
cy.get(
|
||||
'[data-test=frequently-used-columns] > div > div > button > span'
|
||||
).click();
|
||||
|
||||
cy.log('/** Select "id" **/');
|
||||
cy.get(
|
||||
'[data-test=frequently-used-columns] > div > ul > li:nth-child(1) > button > div > div:nth-child(1)'
|
||||
).click();
|
||||
|
||||
cy.log('/** Click on the second column name **/');
|
||||
cy.get('[data-test=column-1]').click();
|
||||
|
||||
cy.log('/** Type column name "name" **/');
|
||||
cy.get('[data-test=column-1]').type('name');
|
||||
|
||||
cy.log('/** Click on the second column column_type **/');
|
||||
cy.get(
|
||||
'[data-test=col-type-1] > div > div > div.css-1hwfws3.col-type-1.add_table_column_selector__value-container'
|
||||
).click();
|
||||
|
||||
cy.log('/** Type text **/');
|
||||
cy.get('#react-select-3-input').type('text');
|
||||
|
||||
cy.log('/** Select option "Text" **/');
|
||||
cy.get('#react-select-3-option-1-2').click();
|
||||
|
||||
cy.log('/** Click "Create table" **/');
|
||||
cy.get('[data-test=table-create] > span').click();
|
||||
|
||||
cy.wait(5000);
|
||||
cy.get('.notification-success').should('be.visible');
|
||||
}
|
||||
);
|
||||
|
||||
it(
|
||||
'tests GraphQL Mutation',
|
||||
{
|
||||
defaultCommandTimeout: 20000,
|
||||
},
|
||||
() => {
|
||||
cy.visit(
|
||||
`http://localhost:${consolePort}/data/${dataSourceDisplayName}/schema/public`
|
||||
);
|
||||
|
||||
cy.log('/** Click API tab **/');
|
||||
cy.get('[data-test=api-tab-link] > p').click();
|
||||
|
||||
cy.log('/** Click GraphiQL Query input field **/');
|
||||
cy.get(
|
||||
'#apiRequestBlock > div > div > div > div > div.graphiql-container > div.editorWrap > div.editorBar > div.queryWrap > section.query-editor > div > div.CodeMirror-scroll > div.CodeMirror-sizer'
|
||||
).click();
|
||||
|
||||
cy.log('/** Select all and delete **/');
|
||||
cy.get('body').type('{cmd}a');
|
||||
cy.get('body').type('{del}');
|
||||
|
||||
cy.log('/** Click GraphiQL query input **/');
|
||||
cy.get(
|
||||
'#apiRequestBlock > div > div > div > div > div.graphiql-container > div.editorWrap > div.editorBar > div.queryWrap > section.query-editor > div > div.CodeMirror-scroll'
|
||||
).click();
|
||||
|
||||
cy.log('/** Select Mutation **/');
|
||||
cy.get(
|
||||
'#apiRequestBlock > div > div > div > div > div.gqlexplorer > div.docExplorerWrap > div.doc-explorer-contents > div > div:nth-child(2) > form > select'
|
||||
).select('mutation');
|
||||
|
||||
cy.log('/** Click "+" **/');
|
||||
cy.get(
|
||||
'#apiRequestBlock > div > div > div > div > div.gqlexplorer > div.docExplorerWrap > div.doc-explorer-contents > div > div:nth-child(2) > form > button > span'
|
||||
).click();
|
||||
|
||||
cy.log(`/** Click "insert_${tableName}_one" **/`);
|
||||
cy.get(
|
||||
`#mutation-MyMutation > div.graphiql-explorer-node.graphiql-explorer-insert_${tableName}_one > span > span.graphiql-explorer-field-view`
|
||||
).click();
|
||||
|
||||
cy.log('/** Click "name" **/');
|
||||
cy.get(
|
||||
`#mutation-MyMutation > div.graphiql-explorer-insert_${tableName}_one > div.graphiql-explorer-node.graphiql-explorer-insert_${tableName}_one > div > div.graphiql-explorer-object > div > div.graphiql-explorer-name > span:nth-child(1) > svg`
|
||||
).click();
|
||||
|
||||
cy.log('/** Click "name" mutation parameter **/');
|
||||
cy.get(
|
||||
'#apiRequestBlock > div > div > div > div > div.graphiql-container > div.editorWrap > div.editorBar > div.queryWrap > section.query-editor > div > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div:nth-child(2) > pre > span > span.cm-string'
|
||||
).click();
|
||||
|
||||
cy.log('/** Type name input parameter **/');
|
||||
cy.get(
|
||||
'#apiRequestBlock > div > div > div > div > div.graphiql-container > div.editorWrap > div.editorBar > div.queryWrap > section.query-editor > div > div:nth-child(1) > textarea'
|
||||
).type('john doe');
|
||||
|
||||
cy.log('/** Click execute **/');
|
||||
cy.get(
|
||||
'#apiRequestBlock > div > div > div > div > div.graphiql-container > div.editorWrap > div.topBarWrap > div > div.execute-button-wrap > button'
|
||||
).click();
|
||||
|
||||
cy.get(
|
||||
'#apiRequestBlock > div > div > div > div > div.graphiql-container > div.editorWrap > div.editorBar > div.resultWrap > section > div > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code'
|
||||
)
|
||||
.should('contain.text', '"data": {')
|
||||
.should('contain.text', `"insert_${tableName}_one": {`)
|
||||
.should('contain.text', '"id":');
|
||||
}
|
||||
);
|
||||
|
||||
it(
|
||||
'tests GraphQL Query',
|
||||
{
|
||||
defaultCommandTimeout: 20000,
|
||||
},
|
||||
() => {
|
||||
cy.visit(
|
||||
`http://localhost:${consolePort}/data/${dataSourceDisplayName}/schema/public`
|
||||
);
|
||||
|
||||
cy.log('/** Click API tab **/');
|
||||
cy.get('[data-test=api-tab-link] > p').click();
|
||||
|
||||
cy.log('/** Click GraphiQL Query input field **/');
|
||||
cy.get(
|
||||
'#apiRequestBlock > div > div > div > div > div.graphiql-container > div.editorWrap > div.editorBar > div.queryWrap > section.query-editor > div > div.CodeMirror-scroll > div.CodeMirror-sizer'
|
||||
).click();
|
||||
|
||||
cy.log('/** Select all and delete **/');
|
||||
cy.get('body').type('{cmd}a');
|
||||
cy.get('body').type('{del}');
|
||||
|
||||
cy.log('/** Click "+" **/');
|
||||
cy.get(
|
||||
'#apiRequestBlock > div > div > div > div > div.gqlexplorer > div.docExplorerWrap > div.doc-explorer-contents > div > div:nth-child(2) > form > button > span'
|
||||
).click();
|
||||
|
||||
cy.log(`/** Expand ${tableName} **/`);
|
||||
cy.get(
|
||||
`#query-MyQuery > div.graphiql-explorer-node.graphiql-explorer-${tableName} > span > span:nth-child(1) > svg > path`
|
||||
).click();
|
||||
|
||||
cy.log('/** Click "id" **/');
|
||||
cy.get(
|
||||
`#query-MyQuery > div.graphiql-explorer-${tableName} > div:nth-child(2) > div.graphiql-explorer-node.graphiql-explorer-id > span > svg`
|
||||
).click();
|
||||
|
||||
cy.log('/** Click "name" **/');
|
||||
cy.get(
|
||||
`#query-MyQuery > div.graphiql-explorer-${tableName} > div:nth-child(2) > div.graphiql-explorer-node.graphiql-explorer-name > span > svg`
|
||||
).click();
|
||||
|
||||
cy.log('/** Click execute **/');
|
||||
cy.get(
|
||||
'#apiRequestBlock > div > div > div > div > div.graphiql-container > div.editorWrap > div.topBarWrap > div > div.execute-button-wrap > button'
|
||||
).click();
|
||||
|
||||
cy.get(
|
||||
'#apiRequestBlock > div > div > div > div > div.graphiql-container > div.editorWrap > div.editorBar > div.resultWrap > section > div > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code'
|
||||
)
|
||||
.should('contain.text', '"data": {')
|
||||
.should('contain.text', `"${tableName}": [`)
|
||||
.should('contain.text', '"id":');
|
||||
}
|
||||
);
|
||||
|
||||
it(
|
||||
'deletes table',
|
||||
{
|
||||
defaultCommandTimeout: 20000,
|
||||
},
|
||||
() => {
|
||||
cy.visit(
|
||||
`http://localhost:${consolePort}/data/${dataSourceDisplayName}/schema/public/tables/${tableName}/modify`,
|
||||
{
|
||||
onBeforeLoad(win) {
|
||||
cy.stub(win, 'prompt').returns(tableName);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
cy.log('/** Click table in the Nav Tree **/');
|
||||
cy.get(`[data-test=${tableName}]`).click();
|
||||
|
||||
cy.log('/** Click Modify tab **/');
|
||||
cy.get('[data-test=table-modify]').click();
|
||||
|
||||
cy.log('/** Click Delete **/');
|
||||
cy.get('[data-test=delete-table] > span').click();
|
||||
|
||||
cy.window().its('prompt').should('be.called');
|
||||
|
||||
cy.get('.notification-success').should('be.visible');
|
||||
}
|
||||
);
|
||||
|
||||
it(
|
||||
'deletes a data source',
|
||||
{
|
||||
defaultCommandTimeout: 20000,
|
||||
},
|
||||
() => {
|
||||
cy.visit(`http://localhost:${consolePort}/data/manage`, {
|
||||
onBeforeLoad(win) {
|
||||
cy.stub(win, 'prompt').returns(dataSourceDisplayName);
|
||||
},
|
||||
});
|
||||
|
||||
cy.log('/** Click "Manage" **/');
|
||||
cy.get('[data-test=sidebar-manage-database]').click();
|
||||
|
||||
cy.log('/** Click "Remove" Data Source **/');
|
||||
cy.get(`[data-test="remove-${dataSourceDisplayName}"]`).click({
|
||||
force: true,
|
||||
});
|
||||
|
||||
cy.window().its('prompt').should('be.called');
|
||||
|
||||
cy.get('.notification-success').should('be.visible');
|
||||
}
|
||||
);
|
||||
});
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
"__version": "10.4.0"
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user