Merge branch 'develop' into ipython

This commit is contained in:
Suraj Shetty 2021-11-12 12:26:59 +05:30 committed by GitHub
commit 619fa164ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 204 additions and 149 deletions

View file

@ -4,6 +4,8 @@
"adminPassword": "admin",
"defaultCommandTimeout": 20000,
"pageLoadTimeout": 15000,
"video": true,
"videoUploadOnPasses": false,
"retries": {
"runMode": 2,
"openMode": 2

View file

@ -33,12 +33,13 @@ context('Control Duration', () => {
cy.get('@dialog').then(dialog => {
let value = dialog.get_value('duration');
expect(value).to.equal(3889800);
cy.hide_dialog();
});
});
it('should hide days or seconds according to duration options', () => {
get_dialog_with_duration(1, 1).as('dialog');
cy.get('.frappe-control[data-fieldname=duration] input').first().click();
cy.get('.frappe-control[data-fieldname=duration] input').first();
cy.get('.duration-input[data-duration=days]').should('not.be.visible');
cy.get('.duration-input[data-duration=seconds]').should('not.be.visible');
});

View file

@ -49,7 +49,7 @@ context('Control Link', () => {
it('should unset invalid value', () => {
get_dialog_with_link().as('dialog');
cy.intercept('POST', '/api/method/frappe.client.validate_link*').as('validate_link');
cy.intercept('POST', '/api/method/frappe.client.validate_link').as('validate_link');
cy.get('.frappe-control[data-fieldname=link] input')
.type('invalid value', { delay: 100 })
@ -61,7 +61,7 @@ context('Control Link', () => {
it('should route to form on arrow click', () => {
get_dialog_with_link().as('dialog');
cy.intercept('POST', '/api/method/frappe.client.validate_link*').as('validate_link');
cy.intercept('POST', '/api/method/frappe.client.validate_link').as('validate_link');
cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link');
cy.get('@todos').then(todos => {
@ -81,11 +81,11 @@ context('Control Link', () => {
it('should fetch valid value', () => {
cy.get('@todos').then(todos => {
cy.visit(`/app/todo/${todos[0]}`);
cy.intercept('GET', '/api/method/frappe.client.get_value*').as('get_value');
cy.intercept('POST', '/api/method/frappe.client.validate_link').as('validate_link');
cy.get('.frappe-control[data-fieldname=assigned_by] input').focus().as('input');
cy.get('@input').type('Administrator', {delay: 100}).blur();
cy.wait('@get_value');
cy.wait('@validate_link');
cy.get('.frappe-control[data-fieldname=assigned_by_full_name] .control-value').should(
'contain', 'Administrator'
);

View file

@ -8,11 +8,7 @@ context('Form', () => {
});
it('create a new form', () => {
cy.visit('/app/todo/new');
cy.get('[data-fieldname="description"] .ql-editor')
.first()
.click()
.type('this is a test todo');
cy.wait(300);
cy.get_field('description', 'Text Editor').type('this is a test todo', {force: true}).wait(200);
cy.get('.page-title').should('contain', 'Not Saved');
cy.intercept({
method: 'POST',
@ -20,29 +16,34 @@ context('Form', () => {
}).as('form_save');
cy.get('.primary-action').click();
cy.wait('@form_save').its('response.statusCode').should('eq', 200);
cy.visit('/app/todo');
cy.wait(300);
cy.get('.title-text').should('be.visible').and('contain', 'To Do');
cy.get('.page-head').findByTitle('To Do').should('exist');
cy.get('.list-row').should('contain', 'this is a test todo');
});
it('navigates between documents with child table list filters applied', () => {
cy.visit('/app/contact');
cy.add_filter();
cy.get('.filter-field .input-with-feedback.form-control').type('123', { force: true });
cy.findByRole('button', {name: 'Apply Filters'}).click({ force: true });
cy.visit('/app/contact/Test Form Contact 3');
cy.clear_filters();
cy.get('.standard-filter-section [data-fieldname="name"] input').type('Test Form Contact 3').blur();
cy.click_listview_row_item(0);
cy.get('.prev-doc').should('be.visible').click();
cy.get('.msgprint-dialog .modal-body').contains('No further records').should('be.visible');
cy.hide_dialog();
cy.get('.next-doc').click();
cy.wait(200);
cy.get('.next-doc').should('be.visible').click();
cy.get('.msgprint-dialog .modal-body').contains('No further records').should('be.visible');
cy.hide_dialog();
cy.contains('Test Form Contact 2').should('not.exist');
cy.get('.title-text').should('contain', 'Test Form Contact 3');
cy.get('#page-Contact .page-head').findByTitle('Test Form Contact 3').should('exist');
// clear filters
cy.visit('/app/contact');
cy.clear_filters();
});
it('validates behaviour of Data options validations in child table', () => {
// test email validations for set_invalid controller
let website_input = 'website.in';

View file

@ -13,8 +13,7 @@ context('List View', () => {
cy.get('.actions-btn-group button').contains('Actions').should('be.visible').click();
cy.get('.dropdown-menu li:visible .dropdown-item .menu-item-label[data-label="Edit"]').click();
cy.get('.modal-body .form-control[data-fieldname="field"]').first().select('Due Date').wait(200);
cy.fill_field('value', '09-28-21', 'Date');
cy.get('.modal-body .form-control[data-fieldname="field"]').first().select('Priority').wait(200);
cy.get('.modal-footer .standard-actions .btn-primary').click();
cy.wait(500);

View file

@ -13,10 +13,10 @@ context('Navigation', () => {
it.only('Navigate to previous page after login', () => {
cy.visit('/app/todo');
cy.findByTitle('To Do').should('be.visible');
cy.get('.page-head').findByTitle('To Do').should('be.visible');
cy.request('/api/method/logout');
cy.reload();
cy.get('.btn-primary').contains('Login').click();
cy.reload().as('reload');
cy.get('@reload').get('.page-card .btn-primary').contains('Login').click();
cy.location('pathname').should('eq', '/login');
cy.login();
cy.visit('/app');

View file

@ -8,6 +8,10 @@ context('Query Report', () => {
'report_type': 'Query Report',
'query': 'select * from tabToDo'
}, true).as('doc');
cy.create_records({
doctype: 'ToDo',
description: 'this is a test todo for query report'
}).as('todos');
});
it('add custom column in report', () => {

View file

@ -14,48 +14,51 @@ context('Recorder', () => {
});
it('Recorder Empty State', () => {
cy.findByTitle('Recorder').should('exist');
cy.get('.page-head').findByTitle('Recorder').should('exist');
cy.get('.indicator-pill').should('contain', 'Inactive').should('have.class', 'red');
cy.findByRole('button', {name: 'Start'}).should('exist');
cy.findByRole('button', {name: 'Clear'}).should('exist');
cy.get('.page-actions').findByRole('button', {name: 'Start'}).should('exist');
cy.get('.page-actions').findByRole('button', {name: 'Clear'}).should('exist');
cy.get('.msg-box').should('contain', 'Inactive');
cy.findByRole('button', {name: 'Start Recording'}).should('exist');
cy.get('.msg-box').should('contain', 'Recorder is Inactive');
cy.get('.msg-box').findByRole('button', {name: 'Start Recording'}).should('exist');
});
it('Recorder Start', () => {
cy.findByRole('button', {name: 'Start'}).click();
cy.get('.page-actions').findByRole('button', {name: 'Start'}).click();
cy.get('.indicator-pill').should('contain', 'Active').should('have.class', 'green');
cy.get('.msg-box').should('contain', 'No Requests');
cy.get('.msg-box').should('contain', 'No Requests found');
cy.visit('/app/List/DocType/List');
cy.intercept('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh');
cy.wait('@list_refresh');
cy.get('.title-text').should('contain', 'DocType');
cy.get('.page-head').findByTitle('DocType').should('exist');
cy.get('.list-count').should('contain', '20 of ');
cy.visit('/app/recorder');
cy.findByTitle('Recorder').should('exist');
cy.get('.result-list').should('contain', '/api/method/frappe.desk.reportview.get');
cy.get('.page-head').findByTitle('Recorder').should('exist');
cy.get('.frappe-list .result-list').should('contain', '/api/method/frappe.desk.reportview.get');
});
it('Recorder View Request', () => {
cy.findByRole('button', {name: 'Start'}).click();
cy.get('.page-actions').findByRole('button', {name: 'Start'}).click();
cy.visit('/app/List/DocType/List');
cy.intercept('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh');
cy.wait('@list_refresh');
cy.get('.title-text').should('contain', 'DocType');
cy.get('.page-head').findByTitle('DocType').should('exist');
cy.get('.list-count').should('contain', '20 of ');
cy.visit('/app/recorder');
cy.get('.list-row-container span').contains('/api/method/frappe').click();
cy.get('.frappe-list .list-row-container span')
.contains('/api/method/frappe')
.should('be.visible')
.click({force: true});
cy.url().should('include', '/recorder/request');
cy.get('form').should('contain', '/api/method/frappe');

View file

@ -23,8 +23,7 @@ context('Report View', () => {
let cell = cy.get('.dt-row-0 > .dt-cell--col-4');
// select the cell
cell.dblclick();
cell.findByRole('checkbox').check({ force: true });
cy.get('.dt-row-0 > .dt-cell--col-5').click();
cell.get('.dt-cell__edit--col-4').findByRole('checkbox').check({ force: true });
cy.wait('@value-update');
cy.get('@doc').then(doc => {
cy.call('frappe.client.get_value', {

View file

@ -8,22 +8,18 @@ context('Timeline', () => {
it('Adding new ToDo, adding new comment, verifying comment addition & deletion and deleting ToDo', () => {
//Adding new ToDo
cy.visit('/app/todo/new-todo-1');
cy.get('[data-fieldname="description"] .ql-editor.ql-blank').type('Test ToDo', {force: true}).wait(200);
cy.get('.page-head .page-actions').findByRole('button', {name: 'Save'}).click();
cy.visit('/app/todo');
cy.click_listview_primary_button('Add ToDo');
cy.findByRole('button', {name: 'Edit in full page'}).click();
cy.findByTitle('New ToDo').should('be.visible');
cy.get('[data-fieldname="description"] .ql-editor').eq(0).type('Test ToDo', {force: true});
cy.wait(200);
cy.findByRole('button', {name: 'Save'}).click();
cy.wait(700);
cy.visit('/app/todo');
cy.get('.level-item.ellipsis').eq(0).click();
cy.click_listview_row_item(0);
//To check if the comment box is initially empty and tying some text into it
cy.get('[data-fieldname="comment"] .ql-editor').should('contain', '').type('Testing Timeline');
//Adding new comment
cy.findByRole('button', {name: 'Comment'}).click();
cy.get('.comment-box').findByRole('button', {name: 'Comment'}).click();
//To check if the commented text is visible in the timeline content
cy.get('.timeline-content').should('contain', 'Testing Timeline');
@ -38,21 +34,17 @@ context('Timeline', () => {
//Discarding comment
cy.click_timeline_action_btn("Edit");
cy.findByRole('button', {name: 'Dismiss'}).click();
cy.click_timeline_action_btn("Dismiss");
//To check if after discarding the timeline content is same as previous
cy.get('.timeline-content').should('contain', 'Testing Timeline 123');
//Deleting the added comment
cy.get('.more-actions > .action-btn').click();
cy.get('.more-actions .dropdown-item').contains('Delete').click();
cy.findByRole('button', {name: 'Yes'}).click();
cy.click_modal_primary_button('Yes');
cy.get('.timeline-message-box .more-actions > .action-btn').click(); //Menu button in timeline item
cy.get('.timeline-message-box .more-actions .dropdown-item').contains('Delete').click({ force: true });
cy.get_open_dialog().findByRole('button', {name: 'Yes'}).click({ force: true });
//Deleting the added ToDo
cy.get('[id="page-ToDo"] .menu-btn-group [data-original-title="Menu"]').click();
cy.get('[id="page-ToDo"] .menu-btn-group .dropdown-item').contains('Delete').click();
cy.findByRole('button', {name: 'Yes'}).click();
cy.get('.timeline-content').should('not.contain', 'Testing Timeline 123');
});
it('Timeline should have submit and cancel activity information', () => {
@ -66,31 +58,32 @@ context('Timeline', () => {
//Adding a new entry for the created custom doctype
cy.fill_field('title', 'Test');
cy.findByRole('button', {name: 'Save'}).click();
cy.findByRole('button', {name: 'Submit'}).click();
cy.click_modal_primary_button('Save');
cy.click_modal_primary_button('Submit');
cy.visit('/app/custom-submittable-doctype');
cy.get('.list-subject > .bold > .ellipsis').eq(0).click();
cy.click_listview_row_item(0);
//To check if the submission of the documemt is visible in the timeline content
cy.get('.timeline-content').should('contain', 'Administrator submitted this document');
cy.findByRole('button', {name: 'Cancel'}).click({delay: 900});
cy.findByRole('button', {name: 'Yes'}).click();
cy.get('[id="page-Custom Submittable DocType"] .page-actions').findByRole('button', {name: 'Cancel'}).click();
cy.get_open_dialog().findByRole('button', {name: 'Yes'}).click();
//To check if the cancellation of the documemt is visible in the timeline content
cy.get('.timeline-content').should('contain', 'Administrator cancelled this document');
//Deleting the document
cy.visit('/app/custom-submittable-doctype');
cy.get('.list-subject > .select-like > .list-row-checkbox').eq(0).click();
cy.findByRole('button', {name: 'Actions'}).click();
cy.get('.actions-btn-group > .dropdown-menu > li > .dropdown-item').contains("Delete").click();
cy.click_modal_primary_button('Yes', {force: true, delay: 700});
cy.select_listview_row_checkbox(0);
cy.get('.page-actions').findByRole('button', {name: 'Actions'}).click();
cy.get('.page-actions .actions-btn-group [data-label="Delete"]').click();
cy.click_modal_primary_button('Yes');
//Deleting the custom doctype
cy.visit('/app/doctype');
cy.get('.list-subject > .select-like > .list-row-checkbox').eq(0).click();
cy.findByRole('button', {name: 'Actions'}).click();
cy.get('.actions-btn-group [data-label="Delete"]').click();
cy.select_listview_row_checkbox(0);
cy.get('.page-actions').findByRole('button', {name: 'Actions'}).click();
cy.get('.page-actions .actions-btn-group [data-label="Delete"]').click();
cy.click_modal_primary_button('Yes');
});
});

View file

@ -341,7 +341,7 @@ Cypress.Commands.add('click_sidebar_button', (btn_name) => {
});
Cypress.Commands.add('click_listview_row_item', (row_no) => {
cy.get('.list-row > .level-left > .list-subject > .bold > .ellipsis').eq(row_no).click({force: true});
cy.get('.list-row > .level-left > .list-subject > .level-item > .ellipsis').eq(row_no).click({force: true});
});
Cypress.Commands.add('click_filter_button', () => {
@ -353,5 +353,9 @@ Cypress.Commands.add('click_listview_primary_button', (btn_name) => {
});
Cypress.Commands.add('click_timeline_action_btn', (btn_name) => {
cy.get('.timeline-message-box .custom-actions > .btn').contains(btn_name).click();
cy.get('.timeline-message-box .actions .action-btn').contains(btn_name).click();
});
Cypress.Commands.add('select_listview_row_checkbox', (row_no) => {
cy.get('.frappe-list .select-like > .list-row-checkbox').eq(row_no).click();
});

38
esbuild/build-cleanup.js Normal file
View file

@ -0,0 +1,38 @@
/* eslint-disable no-console */
const path = require("path");
const fs = require("fs");
const glob = require("fast-glob");
module.exports = {
name: 'build_cleanup',
setup(build) {
build.onEnd(result => {
if (result.errors.length) return;
clean_dist_files(Object.keys(result.metafile.outputs));
});
},
};
function clean_dist_files(new_files) {
new_files.forEach(
file => {
if (file.endsWith(".map")) return;
const pattern = file.split(".").slice(0, -2).join(".") + "*";
glob.sync(pattern).forEach(
file_to_delete => {
if (file_to_delete.startsWith(file)) return;
fs.unlink(path.resolve(file_to_delete), err => {
if (!err) return;
console.error(
`Error deleting ${file.split(path.sep).pop()}`
);
});
}
);
}
);
}

View file

@ -1,18 +1,20 @@
/* eslint-disable no-console */
let path = require("path");
let fs = require("fs");
let glob = require("fast-glob");
let esbuild = require("esbuild");
let vue = require("esbuild-vue");
let yargs = require("yargs");
let cliui = require("cliui")();
let chalk = require("chalk");
let html_plugin = require("./frappe-html");
let rtlcss = require('rtlcss');
let postCssPlugin = require("esbuild-plugin-postcss2").default;
let ignore_assets = require("./ignore-assets");
let sass_options = require("./sass_options");
let {
const path = require("path");
const fs = require("fs");
const glob = require("fast-glob");
const esbuild = require("esbuild");
const vue = require("esbuild-vue");
const yargs = require("yargs");
const cliui = require("cliui")();
const chalk = require("chalk");
const html_plugin = require("./frappe-html");
const rtlcss = require('rtlcss');
const postCssPlugin = require("esbuild-plugin-postcss2").default;
const ignore_assets = require("./ignore-assets");
const sass_options = require("./sass_options");
const build_cleanup_plugin = require("./build-cleanup");
const {
app_list,
assets_path,
apps_path,
@ -26,7 +28,7 @@ let {
get_redis_subscriber
} = require("./utils");
let argv = yargs
const argv = yargs
.usage("Usage: node esbuild [options]")
.option("apps", {
type: "string",
@ -98,9 +100,6 @@ if (WATCH_MODE) {
async function execute() {
console.time(TOTAL_BUILD_TIME);
if (!FILES_TO_BUILD.length) {
await clean_dist_folders(APPS);
}
let results;
try {
@ -231,12 +230,13 @@ function get_files_to_build(files) {
function build_files({ files, outdir }) {
let build_plugins = [
html_plugin,
build_cleanup_plugin,
vue(),
];
return esbuild.build(get_build_options(files, outdir, build_plugins));
}
function build_style_files({ files, outdir, rtl_style=false }) {
function build_style_files({ files, outdir, rtl_style = false }) {
let plugins = [];
if (rtl_style) {
plugins.push(rtlcss);
@ -244,6 +244,7 @@ function build_style_files({ files, outdir, rtl_style=false }) {
let build_plugins = [
ignore_assets,
build_cleanup_plugin,
postCssPlugin({
plugins: plugins,
sassOptions: sass_options
@ -313,24 +314,6 @@ function get_watch_config() {
return null;
}
async function clean_dist_folders(apps) {
for (let app of apps) {
let public_path = get_public_path(app);
let paths = [
path.resolve(public_path, "dist", "js"),
path.resolve(public_path, "dist", "css"),
path.resolve(public_path, "dist", "css-rtl")
];
for (let target of paths) {
if (fs.existsSync(target)) {
// rmdir is deprecated in node 16, this will work in both node 14 and 16
let rmdir = fs.promises.rm || fs.promises.rmdir;
await rmdir(target, { recursive: true });
}
}
}
}
function log_built_assets(results) {
let outputs = {};
for (const result of results) {

View file

@ -87,7 +87,7 @@ def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False, paren
filters = {"name": filters}
try:
fields = json.loads(fieldname)
fields = frappe.parse_json(fieldname)
except (TypeError, ValueError):
# name passed, not json
fields = [fieldname]
@ -407,7 +407,7 @@ def is_document_amended(doctype, docname):
return False
@frappe.whitelist()
def validate_link(doctype: str, docname: str):
def validate_link(doctype: str, docname: str, fields=None):
if not isinstance(doctype, str):
frappe.throw(_("DocType must be a string"))
@ -424,4 +424,26 @@ def validate_link(doctype: str, docname: str):
frappe.PermissionError
)
return frappe.db.get_value(doctype, docname, cache=True)
values = frappe._dict()
values.name = frappe.db.get_value(doctype, docname, cache=True)
fields = frappe.parse_json(fields)
if not values.name or not fields:
return values
try:
values.update(get_value(doctype, fields, docname))
except frappe.PermissionError:
frappe.clear_last_message()
frappe.msgprint(
_("You need {0} permission to fetch values from {1} {2}")
.format(
frappe.bold(_("Read")),
frappe.bold(doctype),
frappe.bold(docname)
),
title=_("Cannot Fetch Values"),
indicator="orange"
)
return values

View file

@ -723,7 +723,7 @@ def run_ui_tests(context, app, headless=False, parallel=True, with_coverage=Fals
frappe.commands.popen("yarn add cypress@^6 cypress-file-upload@^5 @testing-library/cypress@^8 @cypress/code-coverage@^3 --no-lockfile")
# run for headless mode
run_or_open = 'run --browser firefox --record' if headless else 'open'
run_or_open = 'run --browser chrome --record' if headless else 'open'
formatted_command = f'{site_env} {password_env} {coverage_env} {cypress_path} {run_or_open}'
if parallel:

View file

@ -2,9 +2,10 @@ import frappe
from ..role import desk_properties
def execute():
frappe.reload_doctype('user')
frappe.reload_doctype('role')
for role in frappe.get_all('Role', ['name', 'desk_access']):
role_doc = frappe.get_doc('Role', role.name)
for key in desk_properties:
role_doc.set(key, role_doc.desk_access)
role_doc.save()
role_doc.save()

View file

@ -167,10 +167,12 @@ frappe.ui.form.Control = class BaseControl {
}
this.inside_change_event = true;
var set = function(value) {
function set(value) {
me.inside_change_event = false;
return frappe.run_serially([
() => me._validated = true,
() => me.set_model_value(value),
() => delete me._validated,
() => {
me.set_mandatory && me.set_mandatory(value);

View file

@ -444,7 +444,11 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
}
validate(value) {
// validate the value just entered
if(this.df.options=="[Select]" || this.df.ignore_link_validation) {
if (
this._validated
|| this.df.options=="[Select]"
|| this.df.ignore_link_validation
) {
return value;
}
@ -454,39 +458,33 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
validate_link_and_fetch(df, options, docname, value) {
if (!value) return;
return new Promise(async (resolve) => {
const fetch_map = this.fetch_map;
const columns_to_fetch = Object.values(fetch_map);
const fetch_map = this.fetch_map;
const columns_to_fetch = Object.values(fetch_map);
// if default and no fetch, no need to validate
if (!columns_to_fetch.length && df.__default_value === value) {
return resolve(value);
// if default and no fetch, no need to validate
if (!columns_to_fetch.length && df.__default_value === value) {
return value;
}
return frappe.xcall("frappe.client.validate_link", {
doctype: options,
docname: value,
fields: columns_to_fetch,
}).then((response) => {
if (!response || !response.name) return "";
if (!docname || !columns_to_fetch.length) return response.name;
for (const [target_field, source_field] of Object.entries(fetch_map)) {
frappe.model.set_value(
df.parent,
docname,
target_field,
response[source_field],
df.fieldtype,
);
}
const name = await frappe.xcall("frappe.client.validate_link", {
doctype: options,
docname: value
});
if (!name) return resolve("");
if (!docname || !columns_to_fetch.length) return resolve(name);
frappe.db.get_value(
options,
value,
columns_to_fetch,
(response) => {
for (const [target_field, source_field] of Object.entries(fetch_map)) {
frappe.model.set_value(
df.parent,
docname,
target_field,
response[source_field],
df.fieldtype,
);
}
}
).always(() => resolve(name));
return response.name;
});
}

View file

@ -41,7 +41,7 @@
"fuse.js": "^3.4.6",
"highlight.js": "^10.4.1",
"html5-qrcode": "^2.0.11",
"jquery": "3.5.0",
"jquery": "3.6.0",
"js-sha256": "^0.9.0",
"jsbarcode": "^3.9.0",
"localforage": "^1.9.0",

View file

@ -2320,7 +2320,12 @@ jpeg-js@^0.3.2:
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.6.tgz#c40382aac9506e7d1f2d856eb02f6c7b2a98b37c"
integrity sha512-MUj2XlMB8kpe+8DJUGH/3UJm4XpI8XEgZQ+CiHDeyrGoKPdW/8FJv6ku+3UiYm5Fz3CWaL+iXmD8Q4Ap6aC1Jw==
jquery@3.5.0, "jquery@>=2.0.0 <4.0.0":
jquery@3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==
"jquery@>=2.0.0 <4.0.0":
version "3.5.0"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.0.tgz#9980b97d9e4194611c36530e7dc46a58d7340fc9"
integrity sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ==