当前位置: 首页 > news >正文

【D3.js in Action 3 精译_031】3.5.2 DIY实战:在 Observable 平台实现带数据标签的 D3 条形图并改造单元测试模块

当前内容所在位置(可进入专栏查看其他译好的章节内容)

  • 第一部分 D3.js 基础知识
    • 第一章 D3.js 简介(已完结)
      • 1.1 何为 D3.js?
      • 1.2 D3 生态系统——入门须知
      • 1.3 数据可视化最佳实践(上)
      • 1.3 数据可视化最佳实践(下)
      • 1.4 本章小结
    • 第二章 DOM 的操作方法(已完结)
      • 2.1 第一个 D3 可视化图表
      • 2.2 环境准备
      • 2.3 用 D3 选中页面元素
      • 2.4 向选择集添加元素
      • 2.5 用 D3 设置与修改元素属性
      • 2.6 用 D3 设置与修改元素样式
      • 2.7 本章小结
    • 第三章 数据的处理 ✔️
      • 3.1 理解数据(已完结)
      • 3.2 准备数据(已完结)
      • 3.3 将数据绑定到 DOM 元素(已完结)
        • 3.3.1 利用数据给 DOM 属性动态赋值
      • 3.4 让数据适应屏幕(已完结)
        • 3.4.1 比例尺简介(上篇)
        • 3.4.2 线性比例尺(中篇)
          • 3.4.2.1 基于 Mocha 测试 D3 线性比例尺(DIY 实战)
        • 3.4.3 分段比例尺(下篇)
          • 3.4.3.1 使用 Observable 在线绘制 D3 条形图(DIY 实战)
      • 3.5 加注图表标签(上篇)
        • 3.5.1 人物专访:Krisztina Szűcs(下篇)
        • 3.5.2 DIY实战:在 Observable 平台实战演练并进行单元测试 ✔️
      • 3.6 本章小结

文章目录

  • 3.5.2 DIY实战:在 Observable 实现带数据标签的 D3 条形图并改造单元测试模块
    • 1 起因
    • 2 经过
      • 2.1 完成条形图剩余部分——绘制数据标签
      • 2.2 用 AI 提示重构单元测试模块
      • 2.3 集成 Chai.js 的 expect 断言
      • 2.4 导出定制的 MyMocha 类及相关断言方法
    • 3 小结

《D3.js in Action》全新第三版封面

《D3.js in Action》全新第三版封面

前言
本篇不是书中的内容,只是昨晚看了自己翻译的那篇给匈牙利设计师 Krisztina Szűcs 做的人物专访,一时兴起,在 Observable 平台重新实现了一版第 3 章的条形图,顺便把上回遇到的单元测试问题一并解决了。建议大家也多动动手,到 Observable 从头开始敲一遍代码,巩固所学。

3.5.2 DIY实战:在 Observable 实现带数据标签的 D3 条形图并改造单元测试模块

1 起因

学完了第三章,我也在本地实测了一遍,效果还不错。于是就想着同步更新一下放到 Observable 上的版本。没曾想竟然在单元测试模块卡住了:Observable 居然不支持 Mocha.js 这样的测试框架,无法使用全局的 describeit 方法来写测试套件!除了支持 Chai.js 断言库的 CDN 引入,其余效果都得自己封装。网上倒是有几个现成的案例,但要么过于简单,只是粗略对断言模块 expect 方法的封装 1

图 1 对 Jest 的 expect 断言做简单封装的效果图

【图 1 对 Jest 的 expect 断言做简单封装的效果图】

要么又过于复杂 2

图 2 同样基于 Jest 的 expect 断言实现的一套定制测试框架

【图 2 同样基于 Jest 的 expect 断言实现的一套定制测试框架】

而我只希望能用上 describeit,最后将单元测试写到一个测试套件(suite)里,大致长这样:

图 3 希望通过组合 describe 和 it 方法实现的单元测试效果

【图 3 希望通过组合 describe 和 it 方法实现的单元测试效果】

没办法,Observable 这方面还不成熟,还得自力更生。

2 经过

2.1 完成条形图剩余部分——绘制数据标签

参考上一节做好的版本(详见我的《3.4 小节 DIY 实战:使用 Observable 在线绘制 D3 条形图》),先把带标签的 D3 条形图画出来。

和上次一样,先上传 data.csv 原始数据集,然后转成 Observable 可以使用的对象数组:

data = {const csv = await FileAttachment("data.csv").csv({typed: true});return csv.sort((a, b) => d3.descending(a.count, b.count));
}

接着定义两个方向上的比例尺,放到一个 JavaScript 对象里备用:

scales = {const x = d3.scaleLinear().domain([0, d3.max(data, (d) => d.count)]).range([0, 450]);const y = d3.scaleBand().domain(data.map((d) => d.technology)).range([0, 700]).paddingInner(0.2);return { x, y };
}

然后就可以绘制条形图了,定义一个图表变量 chart

chart = {const svg = d3.create("svg").attr("viewBox", "0 0 600 700").attr("width", "100%")// .style('border', '1px solid black');const groups = svg.selectAll("g").data(data).join("g").attr("transform", (d) => `translate(0, ${scales.y(d.technology)})`);// append rectsappendRect(groups);// append tech name labelsappendTechNameLabels(groups);// append count labelsappendCountLabels(groups);// data binding partappendAxisLine(svg);return svg.node();
}

由于要加注两组标签,要用到 SVG 的分组元素(g),这里需要现将数据绑定到每个 <g> 元素上(如第 8 行所示)。然后用 groups 选择集分别完成矩形条、名称标签以及数据标签的绑定与绘制。为了方便查看,我把它们都提到了单独的单元格来处理(践行“单一职责”原则)。

先是技术名称标签。我再原书内容的基础上,把 D3.js 对应的得票数也设置了一些样式(加粗、变色、调整字号):

function appendCountLabels(groups) {// Define predicatesconst target = 'D3.js';const fontSizeHightD3 = ({technology: t}) => (t === target) ? '9px' : '8px';const fontWeightByTechName = ({technology: t}) => (t === target) ? 700 : 400;const fillColorByTechName = ({technology: t}) => (t === target) ? 'yellowgreen' : '#000';// Append labelsgroups.append('text').attr('x', d => 100 + scales.x(d.count) + 4).attr('y', 12).text(d => d.count).style('font-family', 'sans-serif').style('font-weight', fontWeightByTechName).style('font-size', fontSizeHightD3).style('fill', fillColorByTechName);
}

效果还不赖:

图 4 升级版的 D3 数据标签效果

【图 4 升级版的 D3 数据标签效果】

接着绘制纵轴标签(对应各技术名称):

function appendTechNameLabels(groups) {groups.append('text').attr('x', 96).attr('y', 12).attr('text-anchor', 'end').text(d => d.technology).style('font-family', 'sans-serif').style('font-size', '10px');
}

然后是矩形条:

function appendRect(groups) {const byTechName = ({technology: t}) => t === 'D3.js' ? 'yellowgreen' : 'skyblue';groups.append('rect').attr('x', 100).attr('y', 0).attr('height', scales.y.bandwidth()).attr('width', d => scales.x(d.count)).attr('fill', byTechName);
}

最后是纵轴的那条直线:

function appendAxisLine(svg) {svg.append('line').attr('x1', 100).attr('y1', 0).attr('x2', 100).attr('y2', 700).attr('stroke', 'black');
}

然后 Shift + Enter 一键出图:

图 5 最终在 Observable 平台绘制的加注了图表标签的 D3 条形图效果

【图 5 最终在 Observable 平台绘制的加注了图表标签的 D3 条形图效果】

2.2 用 AI 提示重构单元测试模块

接下来才是本篇的重头戏——自己封装一套 describe 方法和 it 方法。还好 Observable 支持断言库 Chai.js 的导入,可能在 Mike Bostock 大神看来,只要把断言结果放到单元格里就行了,干嘛要写成 describe 嵌套 it 的结构呢?对于想用 JS 的循环结构来写测试的码畜的想法,大神可能无暇顾及:

// 这是我精心构建的测试数据(多么优雅~我居然还会用 Map)
testData = new Map([[198, 83],[414, 173],[852, 256], // backup: 852 -> 356[1078, 450]
]);

本来【图1】是出不来效果的,因为 it_old 方法最初的定义是这样的:

/*** Test helper to display test title into the notebook*/
function it_old(title, testFunction) {try {testFunction.call(this);return html`<div style="color:green;" >✓ : ${title || "Test passing "}</div>`;} catch (err) {return html`<div style="color:red;" >× : ${err.message}</div>`;}
}

如果不逐个返回运行的结果,就会乱套:

invalidResults = {it_old("test1", () => expect(2).to.be.lessThan(1));it_old("test2", () => expect(5).to.be.lessThan(1));it_old("test3", () => expect(10).to.be.lessThan(1));it_old("test4", () => expect(100).to.be.lessThan(1));
}

运行单元格后看不到任何报错:

图 6 无法将测试结果正确显示到页面旧版 it 方法

【图 6 无法将测试结果正确显示到页面旧版 it 方法】

这么一来,我要封装的 it 方法和 describe 方法,必须自动收集这样的断言结果才行,而且还得在后台完成,不然太 low,与我的码畜风格相悖。于是我想到了 ES6 引入的 class 语法糖,先把 describeit 定义的回调函数收集到类的一个成员数组,运行的时候再用 this 去挨个遍历它们,结果放到另一个数组,最后用统一的渲染函数交卷,不就搞定了吗?

想法成形,下一步就让机智的 AI 帮我出个 0.1 版吧。果然,不抱太大希望的情况下往往有惊喜,居然帮我把 beforeHooksafterHooks 都实现了(先不论对错,这么端正的态度就值得表扬):

class TestSuite {constructor(name) {this.name = name;this.tests = [];this.beforeHooks = [];this.afterHooks = [];}describe(name, fn) {const suite = new TestSuite(name);fn.call(suite);this.tests.push(suite);}it(name, fn) {this.tests.push({ name, fn });}before(fn) {this.beforeHooks.push(fn);}after(fn) {this.afterHooks.push(fn);}async run() {console.log(`Running suite: ${this.name}`);// Run before hooksfor (const hook of this.beforeHooks) {await hook();}for (const test of this.tests) {if (typeof test.fn === 'function') {try {await test.fn();console.log(`✔️ ${test.name}`);} catch (error) {console.error(`${test.name}`);console.error(error);}} else {// Recursively run nested suitesawait test.run();}}// Run after hooksfor (const hook of this.afterHooks) {await hook();}}
}// 使用示例
const suite = new TestSuite('My Test Suite');suite.describe('Array', function() {this.before(() => {console.log('Setting up before tests...');});this.after(() => {console.log('Cleaning up after tests...');});this.it('should add items', async () => {const arr = [];arr.push(1);if (arr.length !== 1) throw new Error('Test failed');});this.it('should remove items', async () => {const arr = [1];arr.pop();if (arr.length === 0) throw new Error('Test failed');});
});suite.run();

直接放到 Observable 单元格运行,虽然有很多小问题,但总算还像那么回事:

图 7 根据 AI 提示词生成的制定代码效果截图

【图 7 根据 AI 提示词生成的制定代码效果截图】

2.3 集成 Chai.js 的 expect 断言

AI 版本过于粗糙,需要调整几个地方:

  1. 控制台输出需要改为页面显示;
  2. 各单元测试结果需要分别收集起来;
  3. 测试套件和用例描述也得放到结果里;
  4. 统一整体输出样式(颜色、缩进等)。

逐一解决这些小瑕疵,于是就有了 v1.0 版的测试类 MyMocha

// Define customized Mocha class
class MyMocha {constructor(name) {this.name = name;this.tests = [];this.results = [md`<div style="font-weight: 700;">🚩 ${name}</div>`];this.beforeHooks = [];this.afterHooks = [];}describe(name, fn) {const suite = new MyMocha(name);fn.call(suite);this.tests.push(suite);this.results.push(md`<div style="font-weight: 700; text-indent: 1em;">⏳ <i>${name}</i></div>`);}it(name, fn) {this.tests.push({ name, fn });}before(fn) {this.beforeHooks.push(fn);}after(fn) {this.afterHooks.push(fn);}// show the results altogether in markdown formatasync showResults() {await this.run();return md`${this.results}`;}isFunction(fn) {return typeof fn === "function";}async run() {console.log(`Running suite: ${this.name}`);// Run before hooksfor (const hook of this.beforeHooks) {await hook();}for (const test of this.tests) {if (this.isFunction(test.fn)) {try {await test.fn();this.results.push(html`<div style="color: green; text-indent: 2em;">✔️ ${test.name}</div>`);} catch (error) {this.results.push(html`<div style="color:red; text-indent: 2em;">❌ ${test.name}</div>`);this.results.push(html`<div style="color: red; text-indent: 3em;">${error.message}</div>`);}} else {// Recursively run nested suitesawait test.run();}}// Run after hooksfor (const hook of this.afterHooks) {await hook();}}
}

然后把 Chai.js 导入,再把 expect 断言提出来:

chai = import("https://unpkg.com/chai/chai.js");
expect = chai.expect.bind(chai);

写个测试看看:

suite = {const testData = new Map([[198, 83],[414, 173],[852, 256], // backup: 852 -> 356[1078, 450]]);const suite = new MyMocha("DIY mocha test:");const it = suite.it.bind(suite);const describe = suite.describe.bind(suite);describe("Testing horizontal scale for my bar chart:", () => {testData.forEach((expected, domain) => {it(`Pass the value ${domain} to the xScale() function, should return ${expected}.`, () => {const actual = scales.x(domain);const diff = Math.abs(actual - expected);expect(diff, "[Diff Exceeded]").to.be.lessThan(1);});});});return suite.showResults();
}

效果还行:

图 8 集成了 Chai.js 的 expect 断言后的测试用例运行结果

【图 8 集成了 Chai.js 的 expect 断言后的测试用例运行结果】

2.4 导出定制的 MyMocha 类及相关断言方法

既然都测试通过了,就可以考虑放到一个新的 Notebook 里,供其它记事本导入了。咱也模仿一下其他网友的套路,搞个标题和用法示例:

图 9 拟用于导出 MyMocha 类和 expect 断言的通用 Notebook 页面

【图 9 拟用于导出 MyMocha 类和 expect 断言的通用 Notebook 页面】

然后将该页面设置为公开访问,并根据 Observable 的官方文档,用规定的导入语法再写一版测试:

图 10 将 Notebook 记事本页面设置为公开访问

【图 10 将 Notebook 记事本页面设置为公开访问】

图 11 从页面右侧边栏的官方文档找到导入其他记事本单元格的写法

【图 11 从页面右侧边栏的官方文档找到导入其他记事本单元格的写法】

按照官方文档,导入要这么写:

import { MyMocha, expect } from "@anton-playground/combined-unit-tests"

再测一遍,结果发现一个 Bug:渲染完成后没有及时清空本次测试结果,导致重复运行后上次的结果也在里面。于是切回公共页面改改渲染函数的逻辑,勉强算是 v1.1 版吧:

// show the results altogether in markdown format
async showResults() {await this.run();const results = md`${this.results}`;this.tests = this.tests.filter((t) => !this.isFunction(t.fn));this.results = [];return results;
}

再测,大功告成:

图 12 最终通过导入公共记事本的自定义方法实现的测试套件的实际效果

【图 12 最终通过导入公共记事本的自定义方法实现的测试套件的实际效果】

3 小结

虽然成功模拟了 Mocha.js 里的 describeit 原语,但毕竟逻辑过于简单,稍微上点有难度的测试就不够用了,而且写法上也没有 Mocha.js 那么自然,对于锚定的几个 hooks 钩子也无暇验证。这个 Notebook 就算抛砖引玉吧,以后对 TDDBDD 了解得更深入了再来升级。

两个记事本页面我都共享出来,方便大家学习交流(可以 Fork 到自己的工作空间(Workspace)进行修改):

  • 定制的 MyMocha 测试类:https://observablehq.com/@anton-playground/combined-unit-tests
  • 加注标签的条形图并通过线性比例尺单元测试的示例页:https://observablehq.com/@anton-playground/my-bar-chart-with-chaijs


  1. 搜到一篇对 Jest 的 expect 方法的轻量级封装案例,详见:Spencer: Unit testing inside a notebook ↩︎

  2. 详见:Tom Larkworthy: Reactive Unit Testing and Reporting Framework ↩︎

相关文章:

【D3.js in Action 3 精译_031】3.5.2 DIY实战:在 Observable 平台实现带数据标签的 D3 条形图并改造单元测试模块

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…...

华为OD机试真题-字符串分割

题目描述&#xff1a; 给定非空字符串s&#xff0c;将该字符串分割成一些子串&#xff0c;使每个子串的ASCII码值的和均为水仙花数。 1、若分割不成功&#xff0c;则返回0。 2、若分割成功且分割结果不唯一&#xff0c;则返回-1。 3、若分割成功且分割结果唯一&#xff0c;则返…...

编程技巧:提高代码健壮性与可维护性的关键方法(以 Shell 为例)

在脚本编写和自动化工作中,良好的编程技巧对于确保代码的健壮性和可维护性至关重要。以下是一些关键的编程技巧,包括模块化设计、单元测试、版本控制、处理边界条件、错误处理、中间值保存和创建 Flag。本文将通过 Shell 脚本示例来阐述这些技巧的应用。 1. 模块化设计 **定…...

【无标题】ReadableStream is not defined

升级 node 版本到 18 及以上即可解决...

【JVM】高级篇

1 GraalVM 1.1 什么是GraalVM GraalVM是Oracle官方推出的一款高性能JDK&#xff0c;使用它享受比OpenJDK或者OracleJDK更好的性能。 GraalVM的官方网址&#xff1a;https://www.graalvm.org/ 官方标语&#xff1a;Build faster, smaller, leaner applications。 更低的CPU…...

nacos1.4源码-服务发现、心跳机制

nacos的服务发现主要采用服务端主动推送客户端定时拉取&#xff1b;心跳机制通过每5s向服务端发送心跳任务来保活&#xff0c;当超过15s服务端未接收到心跳任务时&#xff0c;将该实例设置为非健康状态&#xff1b;当超过30s时&#xff0c;删除该实例。 1.服务发现 nacos主要采…...

C++ 2D平台游戏开发案例

关于2D平台游戏的C开发案例&#xff0c;包括游戏设计、实现细节、图形渲染和音效处理等内容。虽然无法一次性提供3000字&#xff0c;但我会尽量详细描述各个部分&#xff0c;并确保有足够的深度和广度。 2D平台游戏开发案例 一、游戏设计 游戏概述 游戏名称&#xff1a;“冒险…...

【Webpack--019】TreeShaking

&#x1f913;&#x1f60d;Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-前端领域博主 &#x1f431;‍&#x1f409;若此文你认为写的不错&#xff0c;不要吝啬你的赞扬&#xff0c;求收藏&#xff0c;求评论&#xff0c;求一个大大的赞&#xff01;&#x1f44d;* &#x…...

Docker基本操作命令

Docker 是一个开源的应用容器引擎&#xff0c;允许开发者打包应用以及其依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间不会有任何接口。主要功能是为开发者提供一个简单…...

开源计算器应用的全面测试计划:确保功能性和可靠性

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

uni.requestPayment 支付成功之后会走 wx.onAppRoute

uni.requestPayment 是用于发起微信支付的统一接口&#xff0c;而 wx.onAppRoute 是用于监听小程序的路由变化。当 uni.requestPayment 支付成功后&#xff0c;如果发生了页面跳转或者其他路由变化&#xff0c;wx.onAppRoute 会被触发。这个行为是正常的&#xff0c;因为支付成…...

统⼀服务入口 - Gateway

网关介绍 问题 在 spring cloud 体系中我们通过 Eureka,Nacos 解决了服务注册,服务发现的问题,使⽤Spring Cloud LoadBalance解决了负载均衡的问题,使⽤ OpenFeign 解决了远程调⽤的问题. 但是当前所有微服务的接⼝都是直接对外暴露的,可以直接通过外部访问.为了保证对外服务的…...

QGraphicsWidget Class

Header:#include < QGraphicsWidget > qmake:QT += widgets Since:Qt 4.4 Inherits:QGraphicsObject and QGraphicsLayoutItem Inherited By:QGraphicsProxyWidget This class was introduced in Qt 4.4. Public Types enum anonymous {Type }Properties autoFi…...

探讨最好用的AI工具:从日常到创新的应用

文章目录 引言常用AI工具1. 语音助手2. 图像识别软件3. 机器翻译工具4. 智能客服系统 创新AI应用1. 自动驾驶汽车2. 虚拟试衣间3. 医疗影像分析4. 个性化推荐系统 个人体验分享1. 通义灵码2. 文心一言3. 智能写作助手4. 智能家居设备5. DALLE6. Whisper7. Codex8. Gym9. ChatGP…...

Python系统教程005(字符串的格式化输出)

知识回顾 1、默认情况下&#xff0c;input函数接收的数据是字符串类型。 2、字符串类型的关键词是str。 3、\n和\t都是转义字符&#xff0c;\n用来换行&#xff0c;\t用来留出一段固定长度的空白。 4、type函数能够用来查看变量的数据类型 5、数据类型的转换&#xff0c;举…...

六款电脑远程控制软件分享,2024最热门软件合集,总有一款适合你!速来看!

想要随时随地控制自己的电脑&#xff1f; 无论你是办公需求&#xff0c;还是要远程协助他人&#xff0c;一款好用的远程控制软件绝对少不了。 2024年最热门的六款远程控制软件已经为你准备好&#xff0c;总有一款适合你&#xff0c;赶快往下看吧&#xff01; 1. 安企神系统—…...

优质微信群不再难寻!掌握这些技巧就够了!

在当今信息爆炸的时代&#xff0c;微信群已成为人们交流思想、分享知识、建立人脉的重要平台。无论是专业领域的深入探讨&#xff0c;还是兴趣爱好的自由交流&#xff0c;微信群都能为你提供一个即时互动的虚拟空间。然而&#xff0c;面对海量的微信群信息&#xff0c;如何高效…...

python - mysql操作

Python MySQL 操作 1. 背景介绍 常见的Mysql驱动介绍&#xff1a; MySQL-python&#xff1a;也就是MySQLdb。是对C语言操作MySQL数据库的一个简单封装。遵循了Python DB API v2。但是只支持Python2&#xff0c;目前还不支持Python3。mysqlclient&#xff1a;是MySQL-python的…...

基于Springboot+Vue的服装生产管理信息系统设计与实现(含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 在这个…...

75.【C语言】文件操作(2)

承接74.【C语言】文件操作(1)文章 目录 5.详细阐释文件的打开和关闭 1.流 2.标准流 3.文件指针 FILE 两层含义 附:FILE的头文件 4.操作文件的步骤 1.fopen函数 ​编辑 简写的全称查询 输入&输出的含义 2.fclose函数 3.代码示例 补充:绝对路径和相对路径 注意…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...

【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?

FTP&#xff08;File Transfer Protocol&#xff09;本身是一个基于 TCP 的协议&#xff0c;理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况&#xff0c;主要原因包括&#xff1a; ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...